動的LINQでは、指定されたクラスのフィールドを持つためにどのような変更を行う必要がありますか?
たとえば、次のC#クエリをDLinqで再現するにはどうすればよいですか。
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
を変更する必要があるとDynamicLibrary.cs
しています。これを実現するために変更する方法についていくつかの指示を出すことができます。
更新 :私は私の答えをもう少し広範なブログ投稿に変えました。
Dynamic Linqライブラリを実際に使用したことはありませんが、DynamicLibrary.csコードと、 質問でリンクを提供した別のstackoverflow質問で提供される型クラスの生成をサポートするための変更を確認しました。それらをすべて分析すると、ネストされたnew
-sは、構成でそのまま使用できます。
ただし、クエリは正しいDynamic Linqの言語クエリではないようです。 DLinqのクエリ文字列は C#と同等ではなく 、独自の文法があることに注意してください。
クエリは次のようになるはずです。
var carsPartial = cars.Select("new(name, year, new maker(make.name as name) as make)").ToList();
このStackoverflowの質問をより注意深く読み直すと、実際には、動的な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";
EtvoilÃ、次のようなクエリを構築できるようになります。
var carsPartial = cars.Select("new(name, year, (new your_namespace.maker(make.name as name)) as make)").ToList();
名前空間とクラスパス全体を含む完全な型名を含めるようにしてください。
私の変更を説明するために、 new
かっこと開きかっこの間に識別子があるかどうかを確認するだけです(最初に追加された「if」を参照してください)。もしそうなら、完全にドットで区切られたクラス名を解析し、匿名のnew
sの場合に独自のクラスを構築する代わりに、 Type.GetType
を通じてそのType
を取得しようとします。
私があなたを正しく理解していれば、 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 });