从动态Linq执行延迟的IQueryable <T>? - c#

我正在使用Dynamic Linq执行一些查询(对不起,但这是我唯一的选择)。结果,我得到的是IQueryable而不是IQueryable<T>。就我而言,我想要一个IQueryable<Thing>,其中Thing是具体类型。

我的查询是这样的:

public IQueryable<Thing> Foo(MyContext db)
{
    var rootQuery = db.People.Where(x => x.City != null && x.State != null);
    var groupedQuery = rootQuery.GroupBy("new ( it.City, it.State )", "it", new []{"City", "State"});
    var finalLogicalQuery = groupedQuery.Select("new ( Count() as TotalNumber, Key.City as City, Key.State as State )");
    var executionDeferredResults = finalLogicalQuery.Take(10); // IQueryable

    IQueryable<Thing> executionDeferredTypedThings = ??; // <--- Help here!!!!

    return executionDeferredTypedThings;
}

我的Thing.cs:

public class Thing
{
    public int TotalNumber { get; set; }
    public string City { get; set; }
    public string State { get; set; }
}

是的,我知道没有Dynamic Linq即可完成上述操作,但是我在这里进行了一些可变性的简化。如果返回类型只是IQueryable,我就可以使其具有可变性,但是我不知道如何转换为IQueryable<Thing>,同时保持执行延迟,同时也使Entity Framework满意。我确实有动态Select总是返回看起来像Thing的东西(带有正确的数据)。但是我根本不知道如何返回IQueryable<Thing>,可以在那里使用一些帮助。谢谢!!

尝试失败1

根据Rex M的建议,我现在尝试使用AutoMapper解决此问题(尽管我不致力于此方法,并且愿意尝试其他方法)。对于AutoMapper方法,我这样做是这样的:

IQueryable<Thing> executionDeferredTypedThings = executionDeferredResults.ProjectTo<Thing>(); // <--- Help here!!!!

但这会导致InvalidOperationException:

从DynamicClass2到Thing的地图丢失。使用Mapper.CreateMap创建。

问题是,虽然我已定义Thing,但尚未定义DynamicClass2,因此无法映射它。

尝试失败2

IQueryable<Thing> executionDeferredTypedThings = db.People.Provider.CreateQuery<Thing>(executionDeferredResults.Expression);

这给出了InvalidCastException,并且似乎是上述AutoMapper失败所遇到的根本问题:

无法将类型为'System.Data.Entity.Infrastructure.DbQuery'1 [DynamicClass2]'的对象强制转换为类型为'System.Linq.IQueryable'1 [MyDtos.Thing]'。

参考方案

如果我理解正确,则可以使用以下扩展方法为您完成工作

public static class DynamicQueryableEx
{
    public static IQueryable<TResult> Select<TResult>(this IQueryable source, string selector, params object[] values)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (selector == null) throw new ArgumentNullException("selector");
        var dynamicLambda = System.Linq.Dynamic.DynamicExpression.ParseLambda(source.ElementType, null, selector, values);
        var memberInit = dynamicLambda.Body as MemberInitExpression;
        if (memberInit == null) throw new NotSupportedException();
        var resultType = typeof(TResult);
        var bindings = memberInit.Bindings.Cast<MemberAssignment>()
            .Select(mb => Expression.Bind(
                (MemberInfo)resultType.GetProperty(mb.Member.Name) ?? resultType.GetField(mb.Member.Name),
                mb.Expression));
        var body = Expression.MemberInit(Expression.New(resultType), bindings);
        var lambda = Expression.Lambda(body, dynamicLambda.Parameters);
        return source.Provider.CreateQuery<TResult>(
            Expression.Call(
                typeof(Queryable), "Select",
                new Type[] { source.ElementType, lambda.Body.Type },
                source.Expression, Expression.Quote(lambda)));
    }
}

(附带说明:坦白地说,我不知道values参数是什么意思,但添加了它以匹配相应的DynamicQueryable.Select方法签名。)

所以你的例子将变成这样

public IQueryable<Thing> Foo(MyContext db)
{
    var rootQuery = db.People.Where(x => x.City != null && x.State != null);
    var groupedQuery = rootQuery.GroupBy("new ( it.City, it.State )", "it", new []{"City", "State"});
    var finalLogicalQuery = groupedQuery.Select<Thing>("new ( Count() as TotalNumber, Key.City as City, Key.State as State )");  // IQueryable<Thing>
    var executionDeferredTypedThings = finalLogicalQuery.Take(10);
    return executionDeferredTypedThings;
}

这个怎么运作

这个想法很简单。

Select内部的DynamicQueryable方法实现看起来像这样

public static IQueryable Select(this IQueryable source, string selector, params object[] values)
{
    if (source == null) throw new ArgumentNullException("source");
    if (selector == null) throw new ArgumentNullException("selector");
    LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, null, selector, values);
    return source.Provider.CreateQuery(
        Expression.Call(
            typeof(Queryable), "Select",
            new Type[] { source.ElementType, lambda.Body.Type },
            source.Expression, Expression.Quote(lambda)));
}

它的作用是动态创建选择器表达式并将其绑定到源Select方法。我们采用完全相同的方法,但是在修改了DynamicExpression.ParseLambda调用创建的选择器表达式之后。

唯一的要求是,投影使用“ new(...)”语法,并且投影属性的名称和类型匹配,我认为这适合您的用例。

返回的表达式是这样的

(source) => new TargetClass
{
    TargetProperty1 = Expression1(source),
    TargetProperty2 = Expression2(source),
    ...
}

其中,TargetClass是动态生成的类。

我们想要的只是保留源代码部分,而只是将目标类/属性替换为所需的类/属性。

对于实现,首先将属性分配转换为

var bindings = memberInit.Bindings.Cast<MemberAssignment>()
    .Select(mb => Expression.Bind(
        (MemberInfo)resultType.GetProperty(mb.Member.Name) ?? resultType.GetField(mb.Member.Name),
        mb.Expression));

然后new DynamicClassXXX { ... }被替换为

var body = Expression.MemberInit(Expression.New(resultType), bindings);

实例化类型<?>的泛型类 - java

我正在为SCJP / OCPJP学习,并且遇到了一个对我来说很奇怪的示例问题。该示例代码实例化了两个通用集合:List<?> list = new ArrayList<?>(); List<? extends Object> list2 = new ArrayList<? extends Object>(); …

如何使用Java流在第一个和最后一个嵌套对象之间创建map <k,v>? - java

假设我有以下课程:class A { int id; List<B> b; } class B { int id; } 我想在A.id到B.id的列表之间创建一个映射(Map<Integer, List<Integer>>,其中key = A.id,而List<Integer>对应于每个A的B.id字段的列表)…

Div单击与单选按钮相同吗? - php

有没有一种方法可以使div上的click事件与表单环境中的单选按钮相同?我只希望下面的div提交值,单选按钮很丑代码输出如下:<input id="radio-2011-06-08" value="2011-06-08" type="radio" name="radio_date&#…

故障排除“警告:session_start():无法发送会话高速缓存限制器-标头已发送” - php

我收到警告:session_start()[function.session-start]:无法发送会话缓存限制器-标头已发送(错误输出开始如果我将表单数据提交到其他文件进行处理,则可以正常工作。但是,如果我将表单数据提交到同一页面,则会出现此错误。请建议<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0…

CodeIgniter更新查询被执行两次 - php

我正在使用CodeIgniter 2.2。每次访问页面时,我都必须用+1更新数据库。代码可以工作,但是每次都会增加+2。示例:如果是total views=2,则在单击页面后total views应该是3,但是数据库中的值是4。我确定我在控制器中仅调用一次模型add_one_to_view_image。控制者 function view(){ $view_i…