簡體   English   中英

插入后加載導航屬性

[英]Loading navigation properties after an insert

我在插入后檢索導航屬性時遇到問題。

這是一個正確填充導航屬性的工作示例:

var entities = Context.MyEntities.Where(x => x.Id == myId).ToList();

//entities[0].MyNavProp is populated here...

問題是在插入之后....

var myEntity = new MyEntity 
              {
                //all properties properly filled, no error in insert....
              };

Context.MyEntity.Add(myEntity);

Context.SaveChanges();

//here, myEntity's navigation properties are null.... :(

這是我嘗試過的

Context.SaveChanges();
Context.Attach(myEntity);
Context.Entry(myEntity).Reload();
myEntity = Context.MyEntity.SingleOrDefault(x => x.Id == theId);

盡管如此,導航屬性仍然是 null。

我在Startup設置中使用UseLazyLoadingProxies

如果我在單獨的請求中對數據庫進行簡單查詢,就像這樣

var result = Context.MyEntity.SingleOrDefault(x => x.Id == 31);

我得到了Castle.Proxies.MyEntityProxy類型的實體,但是在插入后立即執行相同的查詢時,我得到了MyEntity類型的實體。

看起來代理延遲加載功能在這種情況下不起作用。

好吧,現在的解決方案是創建一個顯式代理。

代替

var myEntity = new MyEntity 
{
    //all properties properly filled, no error in insert....
};

經過

var myEntity = Context.MyEntity.CreateProxy();
//populate entities, add to context and SaveChanges()....

您的導航屬性將被正確填充,至少在我的情況下是這樣。

UseLazyLoadingProxies要求所有實體類型都是公共的、未密封的、具有虛擬導航屬性並具有公共或受保護的構造函數。

另一個需要注意的重點是在使用UseLazyLoadingProxies從上下文中恢復實體時不要使用AsNoTracking()

我復制了類似的風景,它對我來說是正確的。

實施細節:

Thing實體(類似於子聚合):

public class Thing : Entity<Guid>
{
    internal Thing(Guid id, string name, string type, double value)
    {
        SetId(id);
        SetName(name);
        SetType(type);
        SetValue(value);
    }

    // The default constructor must be explicitly defined, 
    // for use Lazy Loading Proxies.
    protected Thing() { }

    public string Name { get; private set; }
    public string Type { get; private set; }
    public double Value { get; private set; }

    // Setters commented for brevity
}

Whatever實體(根聚合):

public class Whatever : Entity<Guid>
{
    internal Whatever(Guid id, string name, DateTime time, string type)
    {
        SetId(id);
        SetName(name);
        SetTime(time);
        SetType(type);
        Things = new List<Thing>();
    }

    // The default constructor must be explicitly defined, 
    // for use Lazy Loading Proxies.
    protected Whatever() { }

    public string Name { get; private set; }
    //Virtual navigation required
    public virtual ICollection<Thing> Things { get; }
    public DateTime Time { get; private set; }
    public string Type { get; private set; }

    // Setters commented for brevity
}

實體類型配置:

public class WhateverConfig : IEntityTypeConfiguration<Whatever>
{
    public void Configure(EntityTypeBuilder<Whatever> builder)
    {
        // commented for brevity
        builder.HasMany(x => x.Things);
    }
}

國際奧委會:

public static IServiceCollection AddDbContext(this IServiceCollection services, Action<RepositoriesOptions> options)
{
    options.Invoke(RepositoriesOptions);

    return services.AddDbContext<WhateverContext>(dbContextOptions
        => dbContextOptions.UseLazyLoadingProxies()
            .UseSqlite(RepositoriesOptions.ConnectionString, sqliteOptions
                => sqliteOptions.MigrationsAssembly(typeof(WhateverContext).Assembly.GetName().Name)));
}

存儲庫:

public virtual async Task InsertAsync(TEntity entity, CancellationToken cancellationToken)
{
    if (await ExistsAsync(entity.Id, cancellationToken).ConfigureAwait(false)) return;

    await _dbSet.AddAsync(entity, cancellationToken);
    await _context.SaveChangesAsync(true, cancellationToken);
}

服務:

public async Task<TEntity> SaveAsync(TModel model, CancellationToken cancellationToken)
{
    var entity = _mapper.Map<TEntity>(model);
    if (entity.IsValid) await _repository.InsertAsync(entity, cancellationToken);
    return entity;
}

保存后從服務返回的entity

結果

使用的版本:

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.3" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.3" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.3" />    
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.3" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="3.1.3" />
  </ItemGroup>

我希望它能以某種方式幫助你。

我找到了可行的解決方案

調用SaveChanges()后執行以下代碼:

IList<EntityEntry> entries = _context.ChangeTracker.Entries().ToList();
foreach (EntityEntry e in entries)
{
    e.State = Microsoft.EntityFrameworkCore.EntityState.Detached;
}

設置狀態后,我們必須通過First(item => item.Id == id)/FirstOrDefault()或使用其他方法從 DbSet 讀取我們的頂級 Model 項目

在哪里:

  • ChangeTracker 是 DbContext 的一個屬性。
  • EntityEntry 在 Microsoft.EntityFrameworkCore.ChangeTracking 中定義

我的 Ef Core 版本是:

    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.2" />

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM