是DbSet <>。特别小心使用本地的东西吗?(Is DbSet<>.Local

2019-08-17 04:28发布

现在过了几天,我一直在努力从一个存储库(检索我的实体DbContext )。

我想所有的实体保存在一个原子操作。 因此,不同的实体一起代表有价值的东西给我。 如果所有的实体是“有效”的话,我可以将它们保存到数据库中。 实体“A”已经存储在我的仓库,并需要检索到“验证”实体“B”。

这是哪里出了问题就出现了。 我的仓库依靠DbSet<TEntity>与LINQ2SQL(伟大工程类Include()导航属性如)。 但是,该DbSet<TEntity>不包含处于“添加”状态的实体。

所以,我有(据我所知)两个选项:

  • 使用ChangeTracker ,看看哪些实体可用,他们查询到根据自己的一套EntityState
  • 使用DbSet<TEntity>.Local财产。

ChangeTracker似乎涉及一些额外的努力工作得到它的方式工作,这样我可以使用LINQ2SQL到Include()导航性能如

DbSet<TEntity>.Local似乎有点怪我。 它可能只是名称。 我刚才读它没有(比DbSet <>本身更慢)表现非常好东西。 不知道如果这是一个伪命题。

可能有人有显著的EntityFramework经验照耀在这一些轻? 什么是“明智”的路去走? 还是我看到鬼,我应该总是使用.Local的财产?

与代码示例更新


什么不顺心的例子

    public void AddAndRetrieveUncommittedTenant()
    {
        _tenantRepository = new TenantRepository(new TenantApplicationTestContext());

        const string tenantName = "testtenant";

        // Create the tenant, but not call `SaveChanges` yet until all entities are validated 
        _tenantRepository.Create(tenantName);

        //
        // Some other code
        //

        var tenant = _tenantRepository.GetTenants().FirstOrDefault(entity => entity.Name.Equals(tenantName));

        // The tenant will be null, because I did not call save changes yet,
        // and the implementation of the Repository uses a DbSet<TEntity>
        // instead of the DbSet<TEntity>.Local.
        Assert.IsNotNull(tenant);

        // Can I safely use DbSet<TEntity>.Local ? Or should I play 
        // around with DbContext.ChangeTracker instead?
    }

我是多么想用我的一个例子Repository

在我的Repository我有这样的方法:

    public IQueryable<TEntity> GetAll()
    {
        return Context.Set<TEntity>().AsQueryable();
    }

这是我在业务代码中使用这种方式:

    public List<Case> GetCasesForUser(User user)
    {
        return _repository.GetAll().
            Where(@case => @case.Owner.EmailAddress.Equals(user.EmailAddress)).
            Include(@case => @case.Type).
            Include(@case => @case.Owner).
            ToList();
    }

这主要就是为什么我宁愿坚持的原因DbSet变量一样。 我需要的灵活性, Include导航性能。 如果我使用ChangeTracker我检索一个实体List ,这并不让我懒加载相关实体在稍后的时间点。

如果这是接近难以理解的瞎扯淡,那么请让我知道,这样我可以改善这个问题。 我迫切需要一个答案。

THX很多提前!

Answer 1:

如果您希望能够“轻松”的问题对DbSet查询,并将它找到新创建的项目,那么你就需要调用的SaveChanges()创建的每个实体之后。 如果您使用的是风格的方法“工作单位”,以持续性机构工作,其实这是没有问题的,因为你可以有工作单位包住UOW中的所有动作为DB交易(即当UOW创建一个新的TransactionScope创建,并调用commit()就可以了,当UOW完成)。 采用这种结构,改变被发送到数据库,并且将是可见的DbSet,但其他UoWs(你使用任何模隔离级别)不可见。

如果你不希望这样的开销,那么你需要修改代码以使在适当的时候利用当地的(可能涉及望着地方,然后发出反对DbSet查询,如果你没有找到你正在寻找)。 在DbSet的find()方法,也可以在这些情况下非常有用。 它会找到在本地或数据库主密钥的实体。 所以,如果你只需要找到通过主键项,这是非常方便的(并具有性能优势以及)。



Answer 2:

正如特里Coatta,最好的办法中提到,如果你不希望保存记录的第一个将检查这两个来源。

例如:

public Person LookupPerson(string emailAddress, DateTime effectiveDate)
{
    Expression<Func<Person, bool>> criteria = 
        p =>
            p.EmailAddress == emailAddress &&
            p.EffectiveDate == effectiveDate;

    return LookupPerson(_context.ObjectSet<Person>.Local.AsQueryable(), criteria) ?? // Search local
           LookupPerson(_context.ObjectSet<Person>.AsQueryable(), criteria); // Search database
}

private Person LookupPerson(IQueryable<Person> source, Expression<Func<Person, bool>> predicate)
{
    return source.FirstOrDefault(predicate);
}


Answer 3:

对于那些谁进来后,我遇到了一些类似的问题,并决定给.Concat方法一试。 我没有做过广泛的性能测试,以便有人用更多的知识比我应该可以随意插入内容。

从本质上讲,为了正确地打破了功能集成到更小的块,我结束了一个情况,就是我有不知道的连续或以前调用到当前UOW同一方法的方法。 所以我这样做:

var context = new MyDbContextClass();
var emp = context.Employees.Concat(context.Employees.Local).FirstOrDefault(e => e.Name.Contains("some name"));


文章来源: Is DbSet<>.Local something to use with special care?