使用动态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
以使其正常工作,并且可以就如何改变它来实现这一目标。
答案 0 :(得分:2)
更新:我已将我的答案变得更加广泛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)并应用我的更改,我刚刚验证了工作:
找到ParseNew
类的ExpressionParser
方法并将其更改为以下内容:
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";
Etvoilà,您将能够构建如下查询:
var carsPartial = cars.Select("new(name, year, (new your_namespace.maker(make.name as name)) as make)").ToList();
确保包含完整的类型名称,包括整个命名空间+类路径。
为了解释我的变化,它只检查new
和左括号之间是否有一些标识符(参见乞讨时添加的“if”)。如果是这样,我们会解析完整的以点分隔的类名,并尝试通过Type
获取Type.GetType
,而不是在匿名new
s的情况下构建自己的类。
答案 1 :(得分:0)
如果我理解正确,你想创建一个包含class car
和class 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 });