在运行时动态选择LINQ SELECT字段 [英] Dynamically select LINQ SELECT fields at runtime

查看:737
本文介绍了在运行时动态选择LINQ SELECT字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对具有多个链接表的数据库进行LINQ查询,并且需要根据输入返回(选择)不同的字段.

ClassA has ParamA, ParamB, and ICollection<ClassB>ClassBs
ClassB has ParamD, ParamE

Linq查询的部分:

.Select(c => new ClassA()
    {
    ParamA = c.ParamA,
    ParamB = c.ParamB,
    ClassBs = c.ClassBs.Select(p => new ClassB()
        {
        ParamD = p.ParamD,
        ParamE = p.ParamE
        }).ToList()
    }).ToList();

在某些电话上,我只需要ParamA和ParamE.在其他调用上,可能是ParamB和ParamE.我已经可以使用Expressions来完成这项工作,但不能用于ICollection.我尝试使用Dynamic.Linq.Core,但在SELECT中找不到用于更新的任何示例.我更喜欢使用MemberExpressions ...

[更新] 更多上下文:ClassA和ClassB本质上是指向SQL表的EF模型. ClassA与ClassB有一对多的关系,因此,为什么我要以这种方式查询它们.班级A是学生记录(姓名,地址,住所等),班级B是测试记录,学生可以进行多个测试,每个测试都有日期,成绩,等级,最高分数得分,最低分数得分等等. >

我并不总是想要两个表的所有字段,因为可能有100万条记录,因此,为什么我只选择SELECT特定查询和操作所需的内容.

解决方案

这可能有点冗长,但是表达式可以做到这一点,例如:

var typeClassA = typeof(ClassA);
var typeClassB = typeof(ClassB);

var parameterExpressionP = Expression.Parameter(typeClassB, "p");
var newExpression = Expression.New(typeClassB);

var paramDPropertyExpression = Expression.Property(parameterExpressionP, "ParamD");
var paramDMemberBinding = Expression.Bind(typeClassB.GetProperty("ParamD"), paramDPropertyExpression);

var paramEPropertyExpression = Expression.Property(parameterExpressionP, "ParamE");
var paramEMemberBinding = Expression.Bind(typeClassB.GetProperty("ParamE"), paramEPropertyExpression);

var memberInitExpression = Expression.MemberInit(
    newExpression,
    paramDMemberBinding, paramEMemberBinding);

var projectionExpression = Expression.Lambda<Func<ClassB, ClassB>>(memberInitExpression, parameterExpressionP);

var parameterExpressionC = Expression.Parameter(typeClassA, "c");
var selectParamExpression = Expression.Property(parameterExpressionC, "ClassBs");

var selectExpression = Expression.Call(
    typeof(Enumerable),
    nameof(Enumerable.Select),
    new[] { typeClassB, typeClassB },
    selectParamExpression, projectionExpression);

var toListExpression = Expression.Call(
    typeof(Enumerable),
    nameof(Enumerable.ToList),
    new[] { typeClassB },
    selectExpression);

这将创建类似以下内容的表达式:

c.ClassBs.Select(p => new ClassB() {ParamD = p.ParamD, ParamE = p.ParamE}).ToList()

I have a LINQ query to a database, with multiple linked tables, and need to return (SELECT) different fields depending on inputs.

ClassA has ParamA, ParamB, and ICollection<ClassB>ClassBs
ClassB has ParamD, ParamE

Portion of the Linq query:

.Select(c => new ClassA()
    {
    ParamA = c.ParamA,
    ParamB = c.ParamB,
    ClassBs = c.ClassBs.Select(p => new ClassB()
        {
        ParamD = p.ParamD,
        ParamE = p.ParamE
        }).ToList()
    }).ToList();

On some calls, I'll want ParamA and ParamE only. On other calls, perhaps ParamB and ParamE. I've been able to make this work using Expressions but not for the ICollection. I attempted using Dynamic.Linq.Core, but couldn't find any examples for newing up in the SELECT. I would prefer to do this with MemberExpressions...

[UPDATE] A bit more context: ClassA and ClassB are essentially EF Models pointing to SQL tables. ClassA has a one-to-many relationship to ClassB, thus, why I query them in this fashion. ClassA would be a Student record (name, address, home, etc), ClassB would be a Test record, where student may have more than one test, each test has date taken, grade, highest_score_of_class, lowest_score_of_class, and many more.

I don't always want all the fields of both tables, as there may be 1 million records, thus, why I prefer to SELECT only what is needed for the specific query and operation requested.

解决方案

This may be a bit verbose, but Expressions can get that way, for example:

var typeClassA = typeof(ClassA);
var typeClassB = typeof(ClassB);

var parameterExpressionP = Expression.Parameter(typeClassB, "p");
var newExpression = Expression.New(typeClassB);

var paramDPropertyExpression = Expression.Property(parameterExpressionP, "ParamD");
var paramDMemberBinding = Expression.Bind(typeClassB.GetProperty("ParamD"), paramDPropertyExpression);

var paramEPropertyExpression = Expression.Property(parameterExpressionP, "ParamE");
var paramEMemberBinding = Expression.Bind(typeClassB.GetProperty("ParamE"), paramEPropertyExpression);

var memberInitExpression = Expression.MemberInit(
    newExpression,
    paramDMemberBinding, paramEMemberBinding);

var projectionExpression = Expression.Lambda<Func<ClassB, ClassB>>(memberInitExpression, parameterExpressionP);

var parameterExpressionC = Expression.Parameter(typeClassA, "c");
var selectParamExpression = Expression.Property(parameterExpressionC, "ClassBs");

var selectExpression = Expression.Call(
    typeof(Enumerable),
    nameof(Enumerable.Select),
    new[] { typeClassB, typeClassB },
    selectParamExpression, projectionExpression);

var toListExpression = Expression.Call(
    typeof(Enumerable),
    nameof(Enumerable.ToList),
    new[] { typeClassB },
    selectExpression);

This will create an expression something like:

c.ClassBs.Select(p => new ClassB() {ParamD = p.ParamD, ParamE = p.ParamE}).ToList()

这篇关于在运行时动态选择LINQ SELECT字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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