是否可以使用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%服务器端。 支持IComparable
或IEquatable
将要求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); }