是否可以使用IComparable来比较entity framework中的实体?

我有一个实现IComparable的POCO类。

 public interface IEntity : IComparable { long Id { get; set; } Func CompareFunction { get; } } public abstract class BaseEntity : IEntity { public virtual long Id { get; set; } public Func CompareFunction { get { Func compare = EvaluateEquivalency; return compare; } } public static int Compare(BaseEntity left, BaseEntity right) { if (object.ReferenceEquals(left, right)) { return 0; } if (object.ReferenceEquals(left, null)) { return -1; } return left.CompareTo(right); } public static bool operator ==(BaseEntity left, BaseEntity right) { if (object.ReferenceEquals(left, null)) { return object.ReferenceEquals(right, null); } return left.Equals(right); } public static bool operator !=(BaseEntity left, BaseEntity right) { return !(left == right); } public static bool operator <(BaseEntity left, BaseEntity right) { return Compare(left, right) (BaseEntity left, BaseEntity right) { return Compare(left, right) > 0; } public override bool Equals(object obj) { IEntity other; if (!(obj is IEntity)) return false; other = (IEntity)obj; if (object.ReferenceEquals(other, null)) { return false; } return this.CompareTo(other) == 0; } public override int GetHashCode() { return base.GetHashCode(); } public virtual int CompareTo(object obj) { if (obj == null) throw new ArgumentNullException("obj"); if (!(obj is IEntity)) throw new ArgumentException("obj is not an IEntity"); if (this.Id == ((IEntity)obj).Id) return 0; return -1; } private bool EvaluateEquivalency(IEntity toCompare) { return Equals(toCompare); } } 

在我的DbContext有我的POCO类的DbContext

但是,当我执行BaseRepository.Exists()我得到一个System.NotSupportedException

 public class BaseRepository : IRepository where TEntity : class, IEntity { ... private TEntity Exists(TEntity entity) { return Context.DbSet.FirstOrDefult(i => i.CompareTo(entity) == 0); } private TEntity ExistsV2(TEntity entity) { return Context.DbSet.FirstOrDefult(i => i.CompareFunction(entity) == 0); } ... } 

exception堆栈跟踪看起来像……

 System.NotSupportedException was unhandled by user code Message=Unable to create a constant value of type '{My POCO Class}'. Only primitive types or enumeration types are supported in this context. Source=System.Data.Entity StackTrace: at System.Data.Objects.ELinq.ExpressionConverter.ConstantTranslator.TypedTranslate(ExpressionConverter parent, ConstantExpression linq) at System.Data.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq) at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq) at System.Data.Objects.ELinq.ExpressionConverter.EqualsTranslator.TypedTranslate(ExpressionConverter parent, BinaryExpression linq) at System.Data.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq) at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq) at System.Data.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input) at System.Data.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input, DbExpressionBinding& binding) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.FirstPredicateTranslatorBase.Translate(ExpressionConverter parent, MethodCallExpression call) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq) at System.Data.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq) at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq) at System.Data.Objects.ELinq.ExpressionConverter.Convert() at System.Data.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption) at System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption) at System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable.GetEnumerator() at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source) at System.Data.Objects.ELinq.ObjectQueryProvider.b__1[TResult](IEnumerable`1 sequence) at System.Data.Objects.ELinq.ObjectQueryProvider.ExecuteSingle[TResult](IEnumerable`1 query, Expression queryRoot) at System.Data.Objects.ELinq.ObjectQueryProvider.System.Linq.IQueryProvider.Execute[S](Expression expression) at System.Data.Entity.Internal.Linq.DbQueryProvider.Execute[TResult](Expression expression) at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source, Expression`1 predicate) 

当我执行BaseRepository.ExistsV2()我得到一个稍微不同的System.NotSupportedException

 System.NotSupportedException was unhandled by user code HResult=-2146233067 Message=The LINQ expression node type 'Invoke' is not supported in LINQ to Entities. Source=System.Data.Entity StackTrace: at System.Data.Objects.ELinq.ExpressionConverter.NotSupportedTranslator.Translate(ExpressionConverter parent, Expression linq) at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq) at System.Data.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input) at System.Data.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input, DbExpressionBinding& binding) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.FirstPredicateTranslatorBase.Translate(ExpressionConverter parent, MethodCallExpression call) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod) at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq) at System.Data.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq) at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq) at System.Data.Objects.ELinq.ExpressionConverter.Convert() at System.Data.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption) at System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption) at System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable.GetEnumerator() at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source) at System.Data.Objects.ELinq.ObjectQueryProvider.b__1[TResult](IEnumerable`1 sequence) at System.Data.Objects.ELinq.ObjectQueryProvider.ExecuteSingle[TResult](IEnumerable`1 query, Expression queryRoot) at System.Data.Objects.ELinq.ObjectQueryProvider.System.Linq.IQueryProvider.Execute[S](Expression expression) at System.Data.Entity.Internal.Linq.DbQueryProvider.Execute[TResult](Expression expression) at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source, Expression`1 predicate) 

我读过Entity Framework不支持IComparable吗? 反正有这个或有没有人知道这个function是否可以在EF6中使用?

不,你不能这样做。

EF要求所有操作都能够执行100%服务器端。 支持IComparableIEquatable将要求EF能够将任意IL转换为SQL,这是它无法做到的。

否则,它必须将整个未过滤的结果集传递给客户端。 您可以通过使用AsEnumerable()来实现这一目标:

 Context.DbSet.AsEnumerable().FirstOrDefault(i => i.CompareTo(entity) == 0); 

这当然会运行得很慢,所以如果表格有任何显着的大小,我不会推荐它。

不幸的是没有(或者更好的答案不容易)。

entity framework只能使用它知道如何转换为SQL的方法。 这在任何版本的SQL中都不会发生变化。 如果EF了解ICompariable,那真正意味着它需要能够将任意代码片段转换为SQL。

然而,有一些替代品。 您可以定义一个可重用的表达式,该表达式在linq中实现,然后将其应用于SQL中的实体(linq通常可以转换为SQL),这将为您提供类似的行为,但实现起来更复杂。 如果你想试试这个http://www.albahari.com/nutshell/linqkit.aspx,请查看linqkit

不,这是不可能的,将来也永远不可能,因为lComparable是.Net的一部分,其他地方在SQL中没有这样的东西。 Lambda表达式转换为SQL WHERE子句,它们永远不会被执行。

  x ⇒ x.CustomerID == 2 

转换为

  WHERE CustomerID =2 

但是对于您的情况,您可以创建基于reflection的方法。

 private TEntity Exists(TEntity entity) { Type t= typeof(TEntity) ; ParameterExpression pe = Expression.Parameter( t ) ; PropertyInfo p = t.GetProperties().First(x => x.GetCustomAttributes( true ).OfType().Any() ) ; Expression e = Expression.Property( pe , p); e = Expression.Equal( e, p.GetValue(entity, null) ); var l = Expression.Lambda>(e , pe) ; return Context.DbSet.FirstOrDefult(l); } 

根据您的实现,您可能必须将KeyAttribute更改为EdmScalarDataAttribute并检查属性是否具有key = true。

您可以将基本实体设置为IComparable,而不是比较已加载的实体(通过延迟加载可能是个问题)! entity framework不直接支持IComparable接口。

或者你可以这样做:

 public abstract class Entity { public int MyCompare(Entity entity) { .. .. } } private Entity Exists(Entity entity) { return Context.DbSet.ToList().FirstOrDefult(i => i.MyCompare(entity) == 0); }