我使用具有以下搜索功能的LinqKit为EntityFramework 4.0提供了一个搜索存储库:
public IQueryable<T> Search<T>(Expression<Func<T, bool>> predicate)
where T : EntityObject
{
return _unitOfWork.ObjectSet<T>().AsExpandable().Where(predicate);
}
另一个使用IQueryable返回值的类以使用布尔LinqKit PredicateBuilder表达式无法实现的方式对查询进行子集化:
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'作为EntityObject没有定义GUID,所以我不能使用它。对此的自然响应是实际定义SubsetByUser()方法以使用具有GUID属性的约束:
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()方法导致:
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。我可以做这样的假:
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的全部原因是在我们最终确定之前不执行查询。
我甚至试过这个:
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);
}
使用反射来获取运行集合 - 解决编译器错误。我认为这很聪明,但它会导致LINQ异常:
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.
我也可以尝试更改搜索方法:
public IQueryable<T> Search<T>(Expression<Func<T, bool>> predicate)
where T : IRunElement
{
return _unitOfWork.ObjectSet<T>().AsExpandable().Where(predicate);
}
但是这当然不会编译,因为IRunElement不是EntityObject,而ObjectSet将T限制为类。
最后的可能性是简单地使所有参数和返回值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);
}
哪个也有效,但这又不允许我们将实例化延迟到结束。
因此,似乎没有什么可以做到这一点,而无需将所有内容实例化为IEnumerable,然后使用AsQueryable()返回它。有什么方法可以把我放在一起,我错过了吗?
答案 0 :(得分:2)
对此的自然响应是实际定义SubsetByUser()方法以使用具有GUID属性的约束: ... 但这不起作用。我正在使用LinqKit,Expandable()方法导致: System.NotSupportedException:无法将类型'Oasis.DataModel.Arc'强制转换为 输入'Oasis.DataModel.Interfaces.IHaveMetadata'。 LINQ to Entities仅支持 转换实体数据模型基元类型
你非常接近。如果使用ExpressionVisitor
删除自动生成的所有不必要的强制转换(转换为基本类型或已实现的接口),则可以使此工作正常工作。
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.*
函数手动创建表达式树,这样就可以避免首先包含演员表。