Динамический LINQ: указание имени класса в новом предложении - PullRequest
4 голосов
/ 08 января 2012

С помощью Dynamic 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, чтобы это работало и могло бы работать в каком-то направлениио том, как изменить его для достижения этого.

Ответы [ 2 ]

2 голосов
/ 08 января 2012

ОБНОВЛЕНИЕ : я превратил свой ответ в чуть более обширный пост в блоге .

На самом деле я никогда не использовал библиотеку 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();

EDIT:

Перечитывая этот вопрос о стековом потоке Я осознаю, что более точно он не расширяет язык 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";

Et voilà, вы сможете создавать запросы как:

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

Убедитесь, что вы включили полное имя типа, включая все пространство имен + путь к классу.

Чтобы объяснить мои изменения, он просто проверяет, есть ли какой-нибудь идентификатор между new и открывающей скобкой (см. Добавленное «если» в начале). Если это так, мы анализируем полное имя класса через точку и пытаемся получить его Type через Type.GetType вместо создания собственного класса в случае анонимного new s.

0 голосов
/ 08 января 2012

Если я вас правильно понял, вы хотите создать простой анонимный класс, содержащий поля как 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 });
...