2011-08-11 47 views
5

我只是用Entity Framework弄湿了自己的脚,但是我对DBSet对象的一些行为感到有些莫名其妙。当我调用Find()方法时,它似乎意识到我最近添加的(但尚未保存的)项目的集合,但是当我尝试查询DBSet时,它似乎只包含一直存在的项目。有没有简单的方法来解决这个问题?我包括一些代码,我已经做了尝试解决这个问题,但是当它开始通过从我的“添加”变更集的项目进行迭代,我得到这个错误:有没有办法在DBSet的查询中包含本地缓存的项目?

"Unable to create a constant value of type EntityType. Only primitive types ('such as Int32, String, and Guid') are supported in this context."

internal DbContext Context { get; set; } 
protected DbSet<T> DBSet { get; set; } 

public virtual IEnumerable<T> Get(
    Expression<Func<T, bool>> filter = null, 
    Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, 
    bool ignoreCachedChanges = false) 
{ 
    IQueryable<T> oReturn = DBSet; 

    // This block is intended to adjust the queryable source to account for changes 
    if (!ignoreCachedChanges) 
    { 
     oReturn = oReturn.Except(Context.ChangeTracker.Entries<T>().Where(x => x.State == System.Data.EntityState.Deleted).Select(x => x.Entity)); 
     oReturn = oReturn.Union(Context.ChangeTracker.Entries<T>().Where(x => x.State == System.Data.EntityState.Added).Select(x => x.Entity)); 
    } 
    if (filter != null) 
     oReturn = oReturn.Where(filter); 

    if (orderBy != null) 
     return orderBy(oReturn).ToList(); 
    else 
     return oReturn.ToList(); 
} 

// NOTE: Get By ID uses Find which considers the queued-but-not-yet-applied changes 
public virtual T GetByID(object id) 
{ 
    return DBSet.Find(id); 
} 

(在上下文有帮助的情况下,我这样做是因为我想设置一个单元测试,在其中填充我的(本地)上下文,以满足特定测试环境的项目......我很快就不需要应用这些变化,因为他们真的只适用于特定的测试,但我可以,如果需要的话...在这样做,我很惊讶地发现,存储库不包括其查询结果中的变化项目 - 除非我在做FindByID - 所以我也希望我能解决这种明显的不一致性,或者g或者更好的理解为什么这是不可行的或者不是一个好主意? :))

回答

2

异常说明:您不能发送实体类型(您的泛型类型T)到服务器的列表以执行SQL中的ExceptUnion,这些“常量”对象的这些集合。基本上你必须在LINQ to Objects的内存中应用ExceptUnion,这意味着:在之后的已经实现了来自服务器的过滤结果(因此在.ToList()之后的某处)。您必须再次将过滤器应用于上下文中处于Added状态的对象列表。

+0

谢谢!这让我更好地理解发生了什么。另外,如果您碰巧读到:这是我的存储库查询方法的明智方法吗?我会倾向于认为,因为这个“离线”集合具有排队的插入/删除,它应该考虑那些(如DBSet.Find()似乎这样做),但是有没有理由不这样做,我可能会忽略? – Steven

+1

@Steven:'Find'非常特别。它只查询主键。这可以在内存(第1步)和SQL中使用(第2步,如果第1步中没有发现任何内容)。对于其他查询,您必须记住,EF必须使用LINQ to Entities执行查询,然后使用LINQ to Objects查询来构建联合。有些查询甚至不能在LINQ to Objects(例如使用'EntityFunctions')时完成。或者相同的查询可以有不同的行为(例如,字符串搜索的区分大小写)。仅使用一个查询规范执行两个查询并非易事,也不可能。 – Slauma

+1

(续)可能是它没有按照您的意愿实施的原因。这些也是您的通用泛型实现在ChangeTracker中构建DB查询对象和对象的联合的原因:您的'filter'应用于LTO或LTE时可能会给出不同的结果,或者它甚至不适用于LTO。我怀疑一个干净的通用实现是可能的。我会尽量避免你需要查询'ChangeTracker'。如果你真的需要,不要尝试一种通用的方法,只能在你的特定情况下进行,并且必须在特定的查询中进行。 – Slauma

相关问题