我有一个类,它使用EF6从SQLite数据库中检索数据。
上下文正在构造函数中注入,并且相同上下文被多次用于同一个类实例。我正在使用实体框架6的Include
扩展方法在查询中包含子实体。要包含的项目在参数中指定,因此可以根据呼叫者的要求进行更改。
以下是该课程的一个示例:
public class AccountManager
{
private SQLiteContext context;
// Injected Context
public AccountManager(SQLiteContext Context)
{
context = Context;
}
public Account GetAccountById(int AccountId, string ToInclude)
{
return context.Account
.Include(ToInclude)
.Single(a => a.AccountId == AccountId)
}
}
问题在于,如果我在第一个查询(查询A)中包含子实体,它可以正常工作,但如果我在后续查询中包含不同的子实体(使用相同的 context )还包括查询A的子实体。例如:
AccountManager am = new AccountManager(MyContext);
// Run first query
var a1 = am.GetAccountById(1, "Payments");
// Run second query
// The result of this query also includes child entity "Payments"
// because it was added to the context in the previous query
var a2 = am.GetAccountById(1, "Owners");
有没有办法可以防止这种情况发生,并且每个查询的“包含”设置都是唯一处理过的?
答案 0 :(得分:1)
当Entity Framework从数据库中获取实体时,默认行为是将实体添加到其更改跟踪器中。更改跟踪器不仅跟踪单个实体的更改,还跟踪关联的更改。因此,EF还会填充其更改跟踪缓存中的实体可以找到的任何导航属性。
因此,当您向所有者提取帐户时,EF仍会找到所属的付款,并尽可能填写account.Payments
,无论您是否愿意。
我认为在您的情况下,您可以做的最好的事情是在不跟踪的情况下获取实体:
return context.Account.AsNoTracking()
.Include(ToInclude)
.Single(a => a.AccountId == AccountId)
现在EF将填充Payments
或Owners
,但不会将这些实体添加到其缓存中。在N层应用程序中,在大多数情况下最好使用AsNoTracking
,因为几乎不会将更改应用于上下文中的实体,而AsNoTracking
执行得更好,因为更新了更改跟踪器和关系修复是昂贵的过程。
或者,您可以将实体标记为EntityState.Detached
,但我不会走那条路。
最好的方法是为每个商业交易使用新的上下文,这样你就可以向这个方向发展。
答案 1 :(得分:0)
重要提示:如果您有一对多的关系,我建议您不要这样做。这个很贵。我宁愿创建两个不同的查询。
但是,如果你想这样做,我认为一个更好的选择可能是做一个动态查询,非常有用。这样的事情:
public Account GetAccountById(int AccountId, bool includePayments = false, bool includeOwners)
{
var query = context.Account;
if(includePayments)
{
query = query.Include(c => c.Payments).AsQueryable()
}
if(includeOwners)
{
query = query.Include(c => c.Owners).AsQueryable()
}
return query.Single(a => a.AccountId == AccountId)
}
答案 2 :(得分:0)
它实际上是为您执行此操作的实体框架的优化。
实体框架在执行查询时构建数据树。
值得问自己为什么要根据这些信息排除这些信息。