我正在使用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…