强制转换为IQueryable类型以在Linq中连接到实体 [英] Cast a IQueryable type to interface in Linq to Entities
问题描述
我的通用类中有以下方法:
I have the following method in my generic class:
// This is the class declaration
public abstract class BaseService<TEntity, TKey> : IBaseService<TEntity, TKey> where TEntity : class, IEntity<TKey>
// The Method
public IQueryable<TEntity> GetActive()
{
if (typeof(IActivable).IsAssignableFrom(typeof(TEntity)))
{
return this.repository.Get().Cast<IActivable>()
.Where(q => q.Active)
.Cast<TEntity>();
}
else
{
return this.Get();
}
}
这是界面:
public interface IActivable
{
bool Active { get; set; }
}
基本上,TEntity
是一个实体(POCO)类,如果它们具有Active
属性,则可以实现IActivable.我想要该方法返回所有Active
值为true的记录.但是,我有此错误:
Basically, TEntity
is an Entity (POCO) class, that can implement IActivable if they have Active
property. I want to the method to return all records that have Active
value true. However, I have this error:
无法将类型"WebTest.Models.Entities.Product"转换为类型 'Data.IActivable'. LINQ to Entities仅支持投射EDM 基本或枚举类型.
Unable to cast the type 'WebTest.Models.Entities.Product' to type 'Data.IActivable'. LINQ to Entities only supports casting EDM primitive or enumeration types.
我了解为什么会发生此错误.但是关于SO的文章没有针对我的情况的任何有效解决方案. Cast
或其他方法可以实现吗?注意:我不想转换为IEnumerable
,我想保留IQueryable
.
I understand why this error occurs. But the articles on SO does not have any valid solution for my case. Is it achievable with Cast
, or any other way? Note: I do not want to convert to IEnumerable
, I want to keep IQueryable
.
推荐答案
EF表达式解析器无需强制转换即可工作,但是如果不进行强制转换,您将无法编译C#代码(C#会抱怨它不能知道TEntity
具有Active
属性).解决方案是:对c#编译器进行强制转换,而不对EF表达式解析器进行强制转换.
The EF expression parser will work without casting, however you won't be able to compile the C# code without the casting (C# will complain that it doesn't know that TEntity
has an Active
property). The solution is: cast for the c# compiler and not cast for the EF expression parser.
因此,如果确定(正在if
中对其进行检查)该对象实现了IActivable
,则可以使用转换(用于编译)创建表达式,然后在运行时删除转换(这是不必要的)对于EF.对于您的特定情况:
So if you are sure (you are checking it in the if
, so you are) that the object implements IActivable
, you can create the expression with the casting (for compiling) and then remove the castings in runtime (which are unnecessary) for EF. For your particular case:
public IQueryable<TEntity> GetActive()
{
if (typeof(IActivable).IsAssignableFrom(typeof(TEntity)))
{
Expression<Func<TEntity, bool>> getActive = x => ((IActivable)x).Active;
getActive = (Expression<Func<TEntity, bool>>)RemoveCastsVisitor.Visit(getActive);
return this.repository.Get().Where(getActive);
}
else
{
return this.Get();
}
}
表达式访问者是这样实现的:
The expression visitor is implemented like this:
internal class RemoveCastsVisitor : ExpressionVisitor
{
private static readonly ExpressionVisitor Default = new RemoveCastsVisitor();
private RemoveCastsVisitor()
{
}
public new static Expression Visit(Expression node)
{
return Default.Visit(node);
}
protected override Expression VisitUnary(UnaryExpression node)
{
if (node.NodeType == ExpressionType.Convert
&& node.Type.IsAssignableFrom(node.Operand.Type))
{
return base.Visit(node.Operand);
}
return base.VisitUnary(node);
}
}
它只是检查是否需要强制类型转换:如果实际值已经实现了强制类型转换,它只会从表达式中删除转换,EF会正确地选择它.
It just checks if a casting is necessary: if the actual value already implements the type it's casting to, it'll just remove the conversion from the expression, and EF will pick it up correctly.
这篇关于强制转换为IQueryable类型以在Linq中连接到实体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!