简体   繁体   English

C#列表 <string> 入门示例访问Lambda Expression:重构以处理列表

[英]c# List<string> to Lambda Expression with starter example: Refactor to handle the List

I have this: 我有这个:

List<string> fields;

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

I want to get this: 我想得到这个:

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

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

GoofBallLogic had this code that was simliar, ending up with p => p.column 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 );

EDIT: What I am trying to accomplish 编辑:我想完成的事情

I have a "generic" Repository: 我有一个“通用”存储库:

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>> ... 

What to do to set entityKey as an object that can be used in an OrderBy statement since Linq to Entities requires ordering a set before doing a .Skip().Take() 将LinerKey设置为可在OrderBy语句中使用的对象的方法,因为Linq to Entities需要在执行.Skip()。Take()之前对集合进行排序。

Edit: 编辑:

Amazingly, Orderby can take this: 令人惊讶的是,Orderby可以采取以下措施:

p => "field1,field2,field3"

Which allows my code to execute but doesn't actually order the items by the field values. 这允许我的代码执行,但实际上不按字段值对项目进行排序。 It's a first step in TDD I guess: use a literal. 我猜这是TDD的第一步:使用文字。

I found this an interesting problem and took some time to figure it out, and found a relatively easy way to do it. 我发现了一个有趣的问题,花了一些时间来解决,并找到了一种相对简单的方法。

Anyhow, here's an example on how to do a single field sort (i'll use your first field), if you want to sort on more fields you'll have to create expressions for them too and use .ThenBy(xxx) after the usual OrderBy(xxx). 无论如何,这是一个有关如何进行单个字段排序的示例(我将使用第一个字段),如果您想对更多字段进行排序,则还必须为其创建表达式并在之后使用.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();

Now you can do an OrderBy(orderFunc) and it'll sort the list by the property named in fieldname. 现在,您可以执行OrderBy(orderFunc),它将按fieldname中命名的属性对列表进行排序。 Only downside being it only works for string fields (return value of expression). 唯一的缺点是它仅适用于字符串字段(表达式的返回值)。 Could probably work around that too though. 也许也可以解决这个问题。

Fixed to work with any IComparable type: 修复可与任何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();

You cannot do this easily because you cannot construct a new expression for a type that doesn't exist at runtime. 您不能轻易做到这一点,因为您无法为运行时不存在的类型构造new表达式。 (You can have anonymous types in C# because the C# compiler creates the type for you.) (您可以在C#中使用匿名类型,因为C#编译器会为您创建类型。)

If you want to do it the really hard way, you could generate a dynamic assembly and actually create the type you need. 如果您想真正地做到这一点,则可以生成一个动态程序集并实际创建所需的类型。 There is a short example here . 这里有一个简短的例子

I suspect that there is an easier way, though. 我怀疑有更简单的方法。 We would need to know what your goal is (what you need this expression tree for), which you haven't stated. 我们需要知道您的目标(该表达式树需要做什么),而您尚未说明。

From your edited question, it appears that you just want to be able to order by multiple keys. 从您编辑的问题来看,您似乎只想能够通过多个键进行订购。 That is easily possible simply by using .OrderBy() followed by .ThenBy() . 只需使用.OrderBy().ThenBy()就可以轻松实现。 I'm assuming that you are using an IQueryable<E> here: 我假设您在这里使用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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM