IQueryable< T>使用泛型&的EntityObject接口(可能吗?) [英] IQueryable<T> with EntityObject using Generics & Interfaces (Possible?)
问题描述
我有一个使用LinqKit和以下搜索功能的EntityFramework 4.0搜索存储库:
I have a search repository for EntityFramework 4.0 using LinqKit with the following search function:
public IQueryable<T> Search<T>(Expression<Func<T, bool>> predicate)
where T : EntityObject
{
return _unitOfWork.ObjectSet<T>().AsExpandable().Where(predicate);
}
另一个使用IQueryable返回值的类,通过布尔LinqKit PredicateBuilder表达式无法实现的方式将查询子集化:
And another class which uses the IQueryable return value to subset the query in ways that are not possible using the Boolean LinqKit PredicateBuilder expressions:
public IQueryable<T> SubsetByUser<T>(IQueryable<T> set, User user)
where T : EntityObject
{
return set.Join(_searcher.Search<Metadatum>((o) => o.UserGUID == user.GUID),
arc => arc.GUID,
meta => meta.ElementGUID,
(arc, meta) => arc);
}
这里的问题是作为实体对象的'T'没有定义GUID,因此我不能使用它.对此的自然反应是实际上定义SubsetByUser()方法以使用具有GUID属性的约束:
The problem here is that 'T' as EntityObject does not define GUID, so I can't use this. The natural response to this is to actually define the SubsetByUser() method to use a constraint with a GUID property:
public IQueryable<T> SubsetByUser<T>(IQueryable<T> set, User user)
where T : IHaveMetadata
{
return set.Join(_searcher.Search<Metadatum>((o) => o.UserGUID == user.GUID),
arc => arc.GUID,
meta => meta.ElementGUID,
(arc, meta) => arc);
}
但这不起作用.我使用的是LinqKit,而Expandable()方法的结果为:
But this doesn't work. I'm using LinqKit and the Expandable() method results in:
System.NotSupportedException: Unable to cast the type 'Oasis.DataModel.Arc' to
type 'Oasis.DataModel.Interfaces.IHaveMetadata'. LINQ to Entities only supports
casting Entity Data Model primitive types
我需要返回一个IQueryable.我可以做这样的假货:
I need an IQueryable to be returned. I can do a fake like this:
public IQueryable<T> SubsetByUser<T>(IQueryable<T> set, User user)
where T : EntityObject
{
return set.AsEnumerable()
.Join(_searcher.Search<Metadatum>((o) => o.UserGUID == user.GUID),
arc => arc.GUID,
meta => meta.ElementGUID,
(arc, meta) => arc)
.AsQueryable();
}
当然可以,但是这当然也是疯狂的事. (我希望使用IQueryable的全部原因是要等到最后才能执行查询.
Which, of course, works, but which is also, of course, a bat-shit crazy thing to do. (The whole reason I want IQueryable is to not execute the query until we're final.
我什至尝试过:
public IQueryable<T> SubsetByUser<T>(IQueryable<T> set, User user)
where T : EntityObject
{
return set.Join(_searcher.Search<Metadatum>((o) => o.UserGUID == user.GUID),
arc => arc.GetType().GetProperty("GUID").GetValue(arc,null),
meta => meta.ElementGUID,
(arc, meta) => arc);
}
使用反射来获取Runs集合-解决编译器错误.我以为那很聪明,但是会导致LINQ异常:
Which uses reflection to get the Runs collection– working around the compiler error. I thought that was rather clever, but it results in the LINQ exception:
System.NotSupportedException: LINQ to Entities does not recognize the
method 'System.Object GetValue(System.Object, System.Object[])' method,
and this method cannot be translated into a store expression.
我也可以尝试更改搜索方法:
I could also try to change the search method:
public IQueryable<T> Search<T>(Expression<Func<T, bool>> predicate)
where T : IRunElement
{
return _unitOfWork.ObjectSet<T>().AsExpandable().Where(predicate);
}
但这当然不会编译,因为IRunElement不是EntityObject,并且ObjectSet将T约束为一个类.
But this won't compile of course because IRunElement is not an EntityObject, and ObjectSet constrains T as a class.
最后的可能性就是使所有参数和返回值成为IEnumerable:
The final possibility is simply making all the parameters and return values IEnumerable:
public IEnumerable<T> SubsetByUser<T>(IEnumerable<T> set, User user)
where T : EntityObject
{
return set.Join(_searcher.Search<Metadatum>((o) => o.UserGUID == user.GUID),
arc => arc.GetType().GetProperty("GUID").GetValue(arc,null),
meta => meta.ElementGUID,
(arc, meta) => arc);
}
这也可以,但是同样不允许我们将实例化延迟到最后.
Which also works, but which, again, doesn't allow us to delay instantiation until the end.
因此,似乎有很多工作要做,而无需将所有实例都实例化为IEnumerable,然后使用AsQueryable()返回它.有什么办法可以把我想念的东西放在一起吗?
So, it seems like there's little I can do to make this work without instantiating everything as an IEnumerable and then returning it using AsQueryable(). Is there some way I can put this together that I'm missing?
推荐答案
对此的自然反应是实际定义SubsetByUser()方法以使用具有GUID属性的约束: ... 但这是行不通的.我使用的是LinqKit,而Expandable()方法的结果是: System.NotSupportedException:无法将类型"Oasis.DataModel.Arc"强制转换为 键入"Oasis.DataModel.Interfaces.IHaveMetadata". LINQ to Entities仅支持 转换实体数据模型原始类型
The natural response to this is to actually define the SubsetByUser() method to use a constraint with a GUID property: ... But this doesn't work. I'm using LinqKit and the Expandable() method results in: System.NotSupportedException: Unable to cast the type 'Oasis.DataModel.Arc' to type 'Oasis.DataModel.Interfaces.IHaveMetadata'. LINQ to Entities only supports casting Entity Data Model primitive types
您与此非常接近.如果您使用ExpressionVisitor
来删除所有不必要的强制转换(强制转换为基本类型或已实现的接口的广播),则可以使此工作正常进行.
You're very close with that. You can make this work if you use an ExpressionVisitor
that removes all unnecessary casts (casts to a base type or an implemented interface) that are automatically generated.
public IQueryable<T> SubsetByUser<T>(IQueryable<T> set, User user)
where T : IHaveMetadata
{
Expression<Func<T, Guid>> GetGUID = arc => arc.GUID;
GetGUID = (Expression<Func<T, Guid>>)RemoveUnnecessaryConversions.Instance.Visit(GetGUID);
return set.Join(_searcher.Search<Metadatum>((o) => o.UserGUID == user.GUID),
GetGUID,
meta => meta.ElementGUID,
(arc, meta) => arc);
}
public class RemoveUnnecessaryConversions : ExpressionVisitor
{
public static readonly RemoveUnnecessaryConversions Instance = new RemoveUnnecessaryConversions();
protected RemoveUnnecessaryConversions() { }
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);
}
}
或者,使用Expression.*
函数手动创建表达式树,这样就可以避免一开始就使用强制类型转换.
Alternatively, create the expression tree manually using the Expression.*
functions, so that you can avoid including the cast in the first place.
这篇关于IQueryable< T>使用泛型&的EntityObject接口(可能吗?)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!