【问题标题】:Dynamic LINQ: Specifying class name in new clause动态 LINQ:在新子句中指定类名
【发布时间】:2012-02-05 06:27:53
【问题描述】:

使用动态 LINQ,需要进行哪些更改才能获得给定类的字段?

例如,如何在 DLinq 中重现以下 C# 查询:

var carsPartial = cars.Select(c => new {c.year, c.name, make = new maker() {name = c.make.name} }).ToList();

我已应用此答案https://stackoverflow.com/a/1468357/288747 中提到的更改,以允许返回类型为调用类型而不是匿名类型。

类定义如下(如果有帮助的话):

class car
{
    public int vid;
    public int odo;
    public int year;

    public string name;

    public maker make;
}

class maker
{
    public string name;
    public int firstYear;
}

以下不起作用(但我认为很接近,但仍然不起作用,因为我没有对动态 linq 库进行必要的更改,而这正是我所需要的):

var carsPartial = cars.Select("new(name, year, new maker() {name = make.name})").ToList();

但它在new maker() { 处失败(如预期的那样)。

我确定我需要更改 DynamicLibrary.cs 才能使其正常工作,并且可以就如何更改它来实现这一目标提供一些指导。

【问题讨论】:

    标签: c# dynamic-linq


    【解决方案1】:

    更新:我已经把我的答案变成了更广泛的blog post

    我还没有真正使用过 Dynamic Linq 库,但我查看了 DynamicLibrary.cs 代码以及在另一个 stackoverflow question 中提供的支持生成类型类的更改,您在问题中提供了链接。分析它们,似乎嵌套的new-s 应该在您的配置中开箱即用。

    但是,您的查询似乎不是正确的 Dynamic Linq 语言查询。请注意,DLinq 的查询字符串 等同于 C# 并且有自己的语法。

    我相信查询应该读出以下内容:

    var carsPartial = cars.Select("new(name, year, new maker(make.name as name) as make)").ToList();
    

    编辑:

    更仔细地重读this stackoverflow question,我意识到,它实际上并没有扩展Dynamic Linq 的语言来创建新的强类型类的可能性。他们只是将结果放到指定为Select() 的通用参数的类中,而不是在查询字符串中指定它。

    要获得您需要的内容,您需要恢复他们的更改(获取通用 DLinq)并应用我的更改,我刚刚验证可以工作:

    找到ExpressionParser类的ParseNew方法,改成如下:

        Expression ParseNew() {
            NextToken();
    
            bool anonymous = true;
            Type class_type = null;
    
            if (token.id == TokenId.Identifier)
            {
                anonymous = false;
                StringBuilder full_type_name = new StringBuilder(GetIdentifier());
    
                NextToken();
    
                while (token.id == TokenId.Dot)
                {
                    NextToken();
                    ValidateToken(TokenId.Identifier, Res.IdentifierExpected);
                    full_type_name.Append(".");
                    full_type_name.Append(GetIdentifier());
                    NextToken();
                }
    
                class_type = Type.GetType(full_type_name.ToString(), false);    
                if (class_type == null)
                    throw ParseError(Res.TypeNotFound, full_type_name.ToString());
            }
    
            ValidateToken(TokenId.OpenParen, Res.OpenParenExpected);
            NextToken();
            List<DynamicProperty> properties = new List<DynamicProperty>();
            List<Expression> expressions = new List<Expression>();
            while (true) {
                int exprPos = token.pos;
                Expression expr = ParseExpression();
                string propName;
                if (TokenIdentifierIs("as")) {
                    NextToken();
                    propName = GetIdentifier();
                    NextToken();
                }
                else {
                    MemberExpression me = expr as MemberExpression;
                    if (me == null) throw ParseError(exprPos, Res.MissingAsClause);
                    propName = me.Member.Name;
                }
                expressions.Add(expr);
                properties.Add(new DynamicProperty(propName, expr.Type));
                if (token.id != TokenId.Comma) break;
                NextToken();
            }
            ValidateToken(TokenId.CloseParen, Res.CloseParenOrCommaExpected);
            NextToken();
            Type type = anonymous ? DynamicExpression.CreateClass(properties) : class_type; 
            MemberBinding[] bindings = new MemberBinding[properties.Count];
            for (int i = 0; i < bindings.Length; i++)
                bindings[i] = Expression.Bind(type.GetProperty(properties[i].Name), expressions[i]);
            return Expression.MemberInit(Expression.New(type), bindings);
        }
    

    然后,找到类Res并添加如下错误信息:

    public const string TypeNotFound = "Type {0} not found";
    

    等等,您将能够构建如下查询:

    var carsPartial = cars.Select("new(name, year, (new your_namespace.maker(make.name as name)) as make)").ToList();
    

    确保包含完整的类型名称,包括整个命名空间+类路径。

    为了解释我的更改,它只是检查new 和左括号之间是否有一些标识符(请参阅乞求处添加的“if”)。如果是这样,我们会解析以点分隔的完整类名,并尝试通过Type.GetType 获取其Type,而不是在匿名news 的情况下构造自己的类。

    【讨论】:

    • 似乎嵌套的news 只支持一半,它仍然会抛出异常,因为它期望在new 之后有一个( 令牌并且它也不知道maker 的类并且认为它是car 类的一个属性。但绝对是一个开始的东西。
    • 但是您是否正确应用了来自stackoverflow.com/a/1468357/288747 的更改?因为没有它们会抛出你提到的异常。
    • 嗯,另一个问题可能是它将new - 而不是maker - 作为标识符作为car 类的属性。试试"new(name, year, (new maker(make.name as name)))"怎么样?
    • 还有一件事......它不是直接的属性引用,因此我们需要在我们的匿名对象中提供属性的名称 - 试试我更新答案中的查询字符串。
    • 我刚刚重新应用了从我拥有的基本 DynamicLibrary.cs 文件中记录的更改。它(仍然)为new(name, year, make = new maker(make.name as name))No applicable method 'maker' exists in type 'car'new(name, year, new(maker(make.name as name))) 抛出'(' expectednew(name, year, new(maker(make.name as name)))
    【解决方案2】:

    如果我对您的理解正确,您希望创建一个包含来自class carclass maker 的字段的普通匿名类。如果是这种情况,您可以在该类中提供新名称,如下所示:

    var carsPartial = cars.Select(c => new { year = c.year, name = c.name, make_name = c.make.name });
    

    甚至只为冲突的字段提供名称:

    var carsPartial = cars.Select(c => new { c.year, c.name, make_name = c.make.name });
    

    【讨论】:

    • 不是真的,我想要的是在动态 linq 中做第一个查询...(c =&gt; new {c.year, c.name, make = new maker() {name = c.make.name} })(即:作为字符串输入),我已经更新了问题的结尾以更好地反映这一点
    猜你喜欢
    • 1970-01-01
    • 2010-10-04
    • 1970-01-01
    • 1970-01-01
    • 2014-07-31
    • 1970-01-01
    • 2010-10-25
    • 1970-01-01
    • 2023-03-29
    相关资源
    最近更新 更多