2013-11-21 37 views
2

我试图为EntityFramework创建一个通用的CreateOrUpdate函数,其中我的类/表总是有一个ID字段。由于该数据发送到网页并回发到服务器,我完全与上下文断开连接,并且必须使用标准(set.Any/set.FirstOrDefault)方法来检查我的对象是否已经存在。如何阻止EntityFramework获取所有行

该方法在完成工作和根据需要创建/更新方面完美工作。但我发现的是db.Set<T>().FirstOrDefault(whereFunction);从DB中取回所有数据,然后在内存中执行FirstOrDefault。我更喜欢这种情况发生在SQL中,但无论我尝试过什么,我都无法使其工作。

对于如何将FirstOrDefault函数转换为正确的SQL,您有任何建议吗?这样我就不会从数据库中检索太多东西了吗?

此外,我试过First,Any & Count,所有这些都从数据库中获取所有行。

public void CreateOrUpdateEntity<T>(T entity) where T : class 
{ 
    using (var db = new ProjectContext()) 
    { 
     Func<T, bool> whereFunction = m => m.As<dynamic>().ID == entity.As<dynamic>().ID; 
     var firstValue = db.Set<T>().FirstOrDefault(whereFunction); 

     if (firstValue == null) 
     { 
      db.Set<T>().Attach(entity); 
      db.ChangeTracker.Entries<T>().First(e => e.Entity == entity).State = EntityState.Added; 
     } 
     else 
     { 
      db.ChangeTracker.Entries<T>().First(e => e.Entity == entity).State = EntityState.Modified; 
     } 
    } 
} 

回答

4

您应该使用expression而不是Func<T, bool>委托。委托的使用调用在内存中执行的Enumerable.FirstOrDefault(Func<T,TResult>)方法(并要求将所有数据加载到客户端),而不是在服务器端将其翻译为SQL并执行的Queryable.FirstOrDefault(Expression<Func<T, TResult>>)

注意:我对你的委托是否可以被翻译成SQL存有怀疑。

BTW:为了通过ID来获得实体您可以使用方法Find

var firstValue = db.Set<T>().Find(entity.As<dynamic>().ID); 
+1

我不得不来测试你的建议,但您的两个建议,使总的感觉!感觉有点密集,因为我没有发现它!我认为这是EF而不是我的失败! :) 谢谢! – Faraday

+2

@Vijay还有一个想法 - 如果你所有的实体都有ID属性,那么考虑创建基本实体类'public abstract class Entity {public int ID {get;组; }}'为他们,并用'where T:Entity'参数化你的方法,因此你可以避免动态类型。当然,这只有在所有实体都具有相同类型的ID的情况下才有意义。 –

+1

好的想法。我原本打算这样做,但有很多不同的类型。我想我可以不那么懒惰,为每个人编写解决方案......当我有时间看看它是否值得时,我会做一些基准测试。我知道动态不是太好,我倾向于在可能的情况下避免它们......再次感谢您的建议,非常感谢! – Faraday