5

一篇文章中,我要求澄清何时使用DbSet<TEntity>.Local. 我还没有得到答案,我认为这个问题可能包含太多信息。在深入挖掘之后,我可以问一个更具体的问题。

希望你们能解释在我的场景中选择两个选项中的哪一个。

我想达到什么目标?

  1. 我正在尝试从我的Repository. 实体仍处于“添加”状态(即我还没有调用该SaveChanges方法)。我需要这个,因为我想将实体存储在一个原子操作中,所以我必须将SaveChanges调用推迟到验证完整模型之后。

  2. 此外,我需要遵守 IQueryable 接口,因为我有依赖代码使用扩展方法IncludeQueryable延迟加载导航属性。

我尝试了什么?

过去几天发生了很多事情,但归结为以下两种不同的方法:

  • DbSet<TEntity>.Local通过属性检索处于“添加”状态的实体
  • 通过DbContext.ChangeTracker

可视化不同方法的代码示例

    public void AddAndRetrieveUncommittedEntityFromDbContext()
    {
        Database.SetInitializer(new DropCreateDatabaseAlways<TestContext>());
        var testContext = new TestContext();

        // Initialize the entities and store them in the repository
        var tenant = new Tenant { Name = "test", Guid = Guid.NewGuid().ToString() };
        var user = new User { Name = "bas", EmailAddress = "bas@domain.com", Password = "password" };
        tenant.Users.Add(user);
        testContext.Tenants.Add(tenant);

        // NOTE: I did not call `SaveChanges` yet, but still I want to retrieve 
        // the tenant from my repository

        ///////////////////////////////////////////////
        // Alternative one, use the ChangeTracker... //
        ///////////////////////////////////////////////
        IEnumerable<DbEntityEntry<Tenant>> tenants = testContext.ChangeTracker.Entries<Tenant>();
        IQueryable<Tenant> query = tenants.Select(dbEntityEntry => dbEntityEntry.Entity).AsQueryable();
        Tenant storedTenant = query.
            Where(ent => ent.Name.Equals("test")).
            Include(ent => ent.Users).First();

        Assert.IsNotNull(storedTenant.Users.FirstOrDefault()); 
        // hurray, it passes

        ///////////////////////////////////////////////
        // Alternative two, use the 'Local' property //
        ///////////////////////////////////////////////
        IQueryable<Tenant> query2 = testContext.Tenants.Local.AsQueryable();
        Tenant storedTenant2 = query2.
            Where(ent => ent.Name.Equals("test")).
            Include(ent => ent.Users).First();

        Assert.IsNotNull(storedTenant2.Users.FirstOrDefault()); 
        // hurray, it passes

        ////////////////////////////////////////////////
        // For completeness => entity is not in DbSet //
        ////////////////////////////////////////////////

        IQueryable<Tenant> query3 = testContext.Tenants.AsQueryable();
        Tenant storedTenant3 = query3.
            Where(ent => ent.Name.Equals("test")).
            Include(ent => ent.Users).FirstOrDefault();

        Assert.IsNotNull(storedTenant3); 
        // Fails, because the entity is not yet available in the DbSet 'testContext.Tenants'
    }

testContext 非常简单:

public class TestContext : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Tenant> Tenants { get; set; }

    public TestContext() : base("Test1") {}
}

问题

  • 检索所有实体(即包括处于“添加”状态的实体)的建议方法是什么?
  • DbSet<TEntity>.Local和的优缺点是ChangeTracker什么?

非常感谢提前!

4

1 回答 1

5

这真的取决于你想做什么。您的代码都没有专门过滤,DbEntityEntry.State因此我假设您确定上下文跟踪的所有实体都处于添加状态。

如果您不确定所有被跟踪的实体都处于添加状态,那么 ChangeTracker您有机会过滤DbEntityEntrys,EntityState.Added然后获取对要处理的实际实体的引用。

随着Local您获得ObservableCollection<T>实体本身。如果您确定它们都处于添加状态,那么这可能是最简单的。如果您不确定,那么您必须枚举集合并DbEntityEntry从上下文中获取以确保:

foreach(var entity in collection)
{
  if(context.entry(entity).State == EntityState.Added)
  {
  //do stuff...
  }
}

如果这只是为了执行验证,那么还有其他方法可以解决,其中一些可以在这里阅读。

于 2013-02-27T01:35:42.460 回答