c # List <string>в лямбда-выражение с примером для начинающих: Refactor для обработки List - PullRequest
0 голосов
/ 31 октября 2010

У меня есть это:

List<string> fields;

fields[0] = "firstFieldName";
fields[1] = "secondFieldName";
...
fields[n] = "nthFieldName";

Я хочу получить это:

var selector = p => new {p.firstField, p.secondField, ..., p.nthFieldName}

// selector is of type Expression<Func<Entity, object>>

GoofBallLogic имел этот код , который был похож, заканчивающийся p => p.column

// Entity is an object in a diagram from Entity Framework 4
var p = Expression.Parameter(typeof(Entity, "p");

var selector = Expression.Lambda<Func<Entity, string>(
    Expression.Property(p, columnToGroupBy), p );

РЕДАКТИРОВАТЬ: Что я пытаюсь сделать

У меня есть «универсальный» репозиторий:

public class Repository<E, C> : IRepository<E,C>
{
    private C _dc {get;set;} // ObjectContext (Entity Framework 4)
    private string _entityName {get;set;}
    public string entityKeyName {get;private set;}
    public List<string> entityKeys {get;private set;}
    public Expression<Func<E, object>> entityKey {get;private set;}
    private EntityContainer _containerName {get;set;}

    public Repository(C myDC)
    {   _dc = myDC; // TODO: check for null

        // Name of "this" ObjectContext
        _containerName = _dc.MetadataWorkspace.GetEntityContainer(
            _dc.DefaultContainerName, DataSpace.CSpace);

        // Name of "this" Entity
        _entityName = _containerName.BaseEntitySets
            .Where(p => p.ElementType.Name == typeof (E).Name)
            .Select( p => p.Name).FirstOrDefault();

        // String list of the keys
        entityKeys = _containerName
            .BaseEntitySets.First(meta => meta.ElementType.Name == 
                typeof(E).Name)
            .ElementType.KeyMembers.Select(k => k.Name).ToList();

        // Thanks Jon Skeet for this cool comma sep list formula
        entityKeyName = string.Join(",", entityKeys.ToArray() );  

        entityKey = Expression.Lambda<Func<E, object>> ... 

Что делатьустановить entityKey как объект, который можно использовать в операторе OrderBy, поскольку Linq to Entities требует упорядочить набор перед выполнением .Skip (). Take ()

Edit:

Удивительно, Orderbyможет принять это:

p => "field1,field2,field3"

Что позволяет моему коду выполняться, но фактически не упорядочивает элементы по значениям поля.Я думаю, это первый шаг в TDD: используйте литерал.

Ответы [ 3 ]

2 голосов
/ 31 октября 2010

Мне показалось, что это интересная проблема, и я потратил некоторое время на ее решение, и нашел относительно простой способ сделать это.

Во всяком случае, вот пример того, как выполнить сортировку по одному полю (я буду использовать ваше первое поле), если вы хотите отсортировать по нескольким полям, вам придется также создавать выражения для них и использовать .ThenBy (xxx) ) после обычного OrderBy (xxx).

    // Create a parameter which passes the object
    ParameterExpression param = Expression.Parameter(typeof(E), "a");

    // Create body of lambda expression
    Expression body = Expression.PropertyOrField(param, fieldname);

    // Create lambda function
    Expression<Func<E, string>> exp = Expression.Lambda<Func<E, string>>(body, param);

    // Compile it so we can use it
    Func<E, string> orderFunc = exp.Compile();

Теперь вы можете сделать OrderBy (orderFunc), и он будет сортировать список по свойству, названному в fieldname. Единственным недостатком является то, что он работает только для строковых полей (возвращаемое значение выражения). Хотя, возможно, это тоже обойдется.

Исправлено для работы с любым типом IComparable:

        // Create a parameter which passes the field
        ParameterExpression param = Expression.Parameter(typeof(E), "a");

        // Create body of lambda expression
        Expression body = Expression.TypeAs(Expression.PropertyOrField(param, fieldname), typeof(IComparable));

        // Create lambda function
        Expression<Func<E, IComparable>> exp = Expression.Lambda<Func<E, IComparable>>(body, param);

        // Compile it so we can use it
        Func<E, IComparable> orderFunc = exp.Compile();
1 голос
/ 31 октября 2010

Вы не можете сделать это легко, потому что вы не можете создать выражение new для типа, который не существует во время выполнения. (Вы можете иметь анонимные типы в C #, потому что компилятор C # создает тип для вас.)

Если вы хотите сделать это действительно трудно 1005 *, вы можете сгенерировать динамическую сборку и создать нужный вам тип. Вот короткий пример .

Я подозреваю, что есть более простой способ. Нам нужно знать, какова ваша цель (для чего вам нужно это дерево выражений), о котором вы не заявили.

0 голосов
/ 01 ноября 2010

Из вашего отредактированного вопроса выясняется, что вы просто хотите иметь возможность сделать заказ несколькими клавишами. Это легко сделать, просто используя .OrderBy(), а затем .ThenBy(). Я предполагаю, что вы используете IQueryable<E> здесь:

IQueryable<E> query = ...;
IOrderedQueryable<E> ordered = null;

foreach (var key in entityKeys)
{
    // Code from Doggett to construct the lambda expression for one step
    ParameterExpression param = Expression.Parameter(typeof(E), "a");
    var body = Expression.TypeAs(
        Expression.PropertyOrField(param, key),
        typeof(IComparable));
    var exp = Expression.Lambda<Func<E, IComparable>>(body, param);

    if (ordered == null)
        ordered = query.OrderBy(exp);
    else
        ordered = ordered.ThenBy(exp);
}

var finalQuery = (ordered ?? query).Skip(n).Take(m);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...