IQueryable与 EntityObject 一起使用泛型 &接口(可能吗?)
我有一个使用 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);
}
它使用反射来获取 Runs 集合 - 解决编译器错误。我认为这相当聪明,但它导致 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( 返回它),我似乎无能为力。 )有什么方法可以将我所缺少的这些放在一起吗?
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);
}
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);
}
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);
}
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
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();
}
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.
I've even tried this:
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);
}
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);
}
But this won't compile of course because IRunElement is not an EntityObject, and ObjectSet constrains T as a class.
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.
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您对此非常接近。如果您使用一个
ExpressionVisitor
来删除自动生成的所有不必要的转换(转换为基本类型或实现的接口),则可以实现此目的。或者,使用
Expression.*
函数手动创建表达式树,这样您就可以避免首先包含强制转换。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.Alternatively, create the expression tree manually using the
Expression.*
functions, so that you can avoid including the cast in the first place.