我有一个实现IComparable
的POCO类。
public interface IEntity : IComparable
{
long Id { get; set; }
Func<IEntity, bool> CompareFunction { get; }
}
public abstract class BaseEntity : IEntity
{
public virtual long Id { get; set; }
public Func<IEntity, bool> CompareFunction
{
get
{
Func<IEntity, bool> 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) < 0;
}
public static bool operator >(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);
}
}
我的DbSet
中的POCO课程有DbContext
。
但是,当我执行BaseRepository.Exists()
时,我得到System.NotSupportedException
。
public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class, IEntity
{
...
private TEntity Exists(TEntity entity)
{
return Context.DbSet<TEntity>.FirstOrDefult(i => i.CompareTo(entity) == 0);
}
private TEntity ExistsV2(TEntity entity)
{
return Context.DbSet<TEntity>.FirstOrDefult(i => i.CompareFunction(entity) == 0);
}
...
}
异常堆栈跟踪看起来像......
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<T>.GetEnumerator()
at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source)
at System.Data.Objects.ELinq.ObjectQueryProvider.<GetElementFunction>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<T>.GetEnumerator()
at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source)
at System.Data.Objects.ELinq.ObjectQueryProvider.<GetElementFunction>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)
我读过实体框架不支持IComparable
?无论如何还是有人知道这个功能是否可以在EF6中使用?
答案 0 :(得分:3)
不,你不能这样做。
EF要求所有操作都能够执行100%服务器端。支持IComparable
或IEquatable
将要求EF能够将任意IL转换为SQL,而这是不可能的。
否则,它必须将整个未过滤的结果集传递给客户端。您可以使用AsEnumerable()
:
Context.DbSet<TEntity>.AsEnumerable().FirstOrDefault(i => i.CompareTo(entity) == 0);
这当然会运行得很慢,所以如果桌子的尺寸很大,我不会推荐它。
答案 1 :(得分:1)
不幸的是没有(或者更好的答案也不容易)。
实体框架只能使用它知道如何转换为SQL的方法。这在任何版本的SQL中都不会发生变化。如果EF了解ICompariable,那真正意味着它需要能够将任意一段代码转换为SQL。
但是有一些替代方案。您可以定义一个可重用的表达式,该表达式在linq中实现,然后将其应用于SQL中的实体(linq通常可以转换为SQL),这将为您提供类似的行为,但实现起来更复杂。如果你想试试这个http://www.albahari.com/nutshell/linqkit.aspx
,请查看linqkit答案 2 :(得分:1)
不,这是不可能的,将来也永远不可能,因为lComparable是.Net的一部分,其他地方在SQL中没有这样的东西。 Lambda表达式转换为SQL WHERE子句,它们永远不会被执行。
x ⇒ x.CustomerID == 2
转换为
WHERE CustomerID =2
但是对于您的情况,您可以创建基于反射的方法。
private TEntity Exists(TEntity entity)
{
Type t= typeof(TEntity) ;
ParameterExpression pe = Expression.Parameter( t ) ;
PropertyInfo p = t.GetProperties().First(x => x.GetCustomAttributes( true ).OfType<KeyAttribute>().Any() ) ;
Expression e = Expression.Property( pe , p);
e = Expression.Equal( e, p.GetValue(entity, null) );
var l = Expression.Lambda<Func <TEntity, boot>>(e , pe) ;
return Context.DbSet<TEntity>.FirstOrDefult(l);
}
根据您的实现,您可能需要将KeyAttribute更改为EdmScalarDataAttribute&amp;检查属性是否有key = true。
答案 3 :(得分:0)
您可以将基本实体设置为IComparable,而不是比较已加载的实体(通过延迟加载可能是个问题)!实体框架不直接支持IComparable接口。
或者你可以这样做:
public abstract class Entity
{
public int MyCompare(Entity entity)
{
..
..
}
}
private Entity Exists(Entity entity)
{
return Context.DbSet<Entity>.ToList().FirstOrDefult(i => i.MyCompare(entity) == 0);
}