IQueryable< T>使用泛型&的EntityObject接口(可能吗?) [英] IQueryable<T> with EntityObject using Generics & Interfaces (Possible?)

查看:115
本文介绍了IQueryable< T>使用泛型&的EntityObject接口(可能吗?)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用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&lt; T&gt;使用泛型&amp;的EntityObject接口(可能吗?)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆