DbSet <tentity> .Local vs DbContext.ChangeTracker </tentity>

时间:2013-02-26 23:04:37

标签: c# entity-framework repository dbcontext

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

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

我想达到什么目的?

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

  2. 此外,我需要符合IQueryable接口,因为我的依赖代码使用Include上的扩展方法Queryable来延迟加载导航属性。

  3. 我尝试了什么?

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

    • 通过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>.LocalChangeTracker有哪些优缺点?

    许多人提前感谢!

1 个答案:

答案 0 :(得分:3)

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

如果您不确定所有跟踪的实体是否处于添加状态,那么ChangeTracker会让您有机会按DbEntityEntry过滤EntityState.Added,然后获取对实际实体的引用工作。

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

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

如果要执行验证,那么还有其他方法可以解决,其中一些可以阅读here