18

几天来,我一直在努力从存储库 ( 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我检索 a 中的实体List,这不允许我在以后的时间点延迟加载相关实体。

如果这接近于难以理解的废话*t,请告诉我,以便我改进问题。我迫切需要一个答案。

提前很多!

4

3 回答 3

16

如果您希望能够“轻松地”对 DbSet 发出查询并让它找到新创建的项目,那么您需要在创建每个实体后调用 SaveChanges()。如果您使用“工作单元”样式的方法来处理持久实体,这实际上没有问题,因为您可以让工作单元将 UoW 中的所有操作包装为数据库事务(即,当 UoW已创建,并在 UoW 完成时对其调用 Commit())。使用这种结构,更改将发送到 DB,并且将对 DbSet 可见,但对其他 UoW 不可见(以您使用的任何隔离级别为模)。

如果您不想要这样的开销,那么您需要修改您的代码以在适当的时候使用 Local(这可能涉及查看 Local,然后如果您没有找到您想要的内容,则针对 DbSet 发出查询正在寻找)。DbSet 上的 Find() 方法在这些情况下也很有帮助。它将通过本地或数据库中的主键查找实体。因此,如果您只需要通过主键定位项目,这非常方便(并且还具有性能优势)。

于 2013-12-06T18:34:17.913 回答
7

正如 Terry 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);
}
于 2016-09-22T23:06:16.273 回答
0

对于后来的人,我遇到了一些类似的问题,并决定尝试 .Concat 方法。我没有进行广泛的性能测试,所以比我有更多知识的人可以随意插话。

本质上,为了正确地将功能分解成更小的块,我最终遇到了一种情况,即我有一个方法不知道当前 UoW 中对同一方法的连续或先前调用。所以我这样做了:

var context = new MyDbContextClass();
var emp = context.Employees.Concat(context.Employees.Local).FirstOrDefault(e => e.Name.Contains("some name"));
于 2014-05-15T18:29:14.653 回答