如何转换这个linq表达式? [英] How can I transform this linq expression?

查看:107
本文介绍了如何转换这个linq表达式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

说我有一个我想要查询排名的实体:

  public class Person:Entity 
{
public int Id {get;保护套}
public string Name {get;组; }
public DateTime Birthday {get;组;
}

在我的查询中,我有以下内容:

 表达式< Func< Person,object>> orderBy = x => x.Name; 

var dbContext = new MyDbContext();

var keyword =term;
var startsWithResults = dbContext.People
.Where(x => x.Name.StartsWith(keyword))
.Select(x => new {
Rank = 1 ,
Entity = x,
});
var containsResults = dbContext.People
.Where(x =>!startsWithResults.Select(y => y.Entity.Id).Contains(x.Id))
.Where (x => x.Name.Contains(keyword))
.Select(x => new {
Rank = 2,
Entity = x,
});

var rankingResults = startsWithResults.Concat(containsResults)
.OrderBy(x => x.Rank);

// TODO:根据orderBy表达式在

dbContext.Dispose();

我已经尝试订购结果,然后选择匿名对象与 Rank 属性,但排序最终会丢失。似乎linq对实体丢弃单独集合的顺序,并在 Concat Union 期间转换回自然排序



我认为我可以做的是动态地将 orderBy 变量中定义的表达式从code> x => x.Name to x => x.Entity.Name ,但我不知道如何:

  if(orderBy!= null)
{
var convertedExpression = ???
rankingResults = rankingResults.ThenBy(convertedExpression);
}

我可以如何使用 Expression.Lambda 包裹 x => x.Name into x => x.Entity.Name ?当我硬编码 x => x.Entity.Name 进入 ThenBy 我得到我想要的订单,但$ code> orderBy 由查询的调用类提供,所以我不想对其进行硬编码。在上面的例子中,为了简单起见,我将其硬编码。

解决方案

这应该有帮助。然而,您将要具体了解匿名类型以使其工作。我的LinqPropertyChain将不会使用它,因为它将难以创建 Expression< Func< Anonymous,Person> ,而它仍然是匿名的。

 表达式< Func< Person,object>> orderBy = x => x.Name; 

using(var dbContext = new MyDbContext())
{
var keyword =term;
var startsWithResults = dbContext.People
.Where(x => x.Name.StartsWith(keyword))
.Select(x => new {
Rank = 1 ,
Entity = x,
});
var containsResults = dbContext.People
.Where(x =>!startsWithResults.Select(y => y.Entity.Id).Contains(x.Id))
.Where (x => x.Name.Contains(keyword))
.Select(x => new {
Rank = 2,
Entity = x,
});


var rankingResults = startsWithResults.Concat(containsResults)
.OrderBy(x => x.Rank)
.ThenBy(LinqPropertyChain.Chain(x => ; x.Entity,orderBy));

// TODO:根据orderBy表达式在

}

上公布静态类LinqPropertyChain
{

public static Expression& Func< TInput,TOutput>>链条< TInput,TOutput,TIntermediate>(
表达式< Func< TInput,TIntermediate>>外部,
表达式&FunC< TIntermediate,TOutput>> inner

{

Console.WriteLine(inner);
Console.WriteLine(outter);
var visitor = new Visitor(new Dictionary< ParameterExpression,Expression>
{
{inner.Parameters [0],outter.Body}
});

var newBody = visitor.Visit(inner.Body);
Console.WriteLine(newBody);
返回Expression.Lambda }

private class Visitor:ExpressionVisitor
{
private readonly Dictionary< ParameterExpression,Expression> _替代;

public Visitor(Dictionary< ParameterExpression,Expression> replacement)
{
_replacement = replacement;


protected override表达式访问参数(ParameterExpression节点)
{
if(_replacement.ContainsKey(node))
return _replacement [node];
else
{
返回节点;
}
}
}
}

使用较少的Explicite泛型来实现此目的。

 表达式< Func< Person,object>> orderBy = x => x.Name; 
表达式< Func< Foo,Person>> personExpression = x => x.Person;

var helper = new ExpressionChain(personExpression);
var chained = helper.Chain(orderBy).Expression;


//在此定义其他方法和类
public class ExpressionChain< TInput,TOutput>
{
private readonly表达式< Func< TInput,TOutput>> _表达;
public ExpressionChain(Expression& Func< TInput,TOutput>> expression)
{
_expression = expression;
}

public Expression< Func< TInput,TOutput>>表达式{get {return _expression; }}

public ExpressionChain< TInput,TChained>链与LT; TChained>
(表达式& Func< TOutput,TChained>> chainedExpression)
{
var visitor = new Visitor(new Dictionary< ParameterExpression,Expression>
{
{_expression 。参数[0],chainedExpression.Body}
});
var lambda = Expression.Lambda 返回新的ExpressionChain(lambda);
}

private class Visitor:ExpressionVisitor
{
private readonly Dictionary< ParameterExpression,Expression> _替代;

public Visitor(Dictionary< ParameterExpression,Expression> replacement)
{
_replacement = replacement;


protected override表达式访问参数(ParameterExpression节点)
{
if(_replacement.ContainsKey(node))
return _replacement [node];
else
{
返回节点;
}
}
}
}


Say I have an entity that I want to query with ranking applied:

public class Person: Entity
{
    public int Id { get; protected set; }
    public string Name { get; set; }
    public DateTime Birthday { get; set; }
}

In my query I have the following:

Expression<Func<Person, object>> orderBy = x => x.Name;

var dbContext = new MyDbContext();

var keyword = "term";
var startsWithResults = dbContext.People
    .Where(x => x.Name.StartsWith(keyword))
    .Select(x => new {
        Rank = 1,
        Entity = x,
    });
var containsResults = dbContext.People
    .Where(x => !startsWithResults.Select(y => y.Entity.Id).Contains(x.Id))
    .Where(x => x.Name.Contains(keyword))
    .Select(x => new {
        Rank = 2,
        Entity = x,
    });

var rankedResults = startsWithResults.Concat(containsResults)
    .OrderBy(x => x.Rank);

// TODO: apply thenby ordering here based on the orderBy expression above

dbContext.Dispose();

I have tried ordering the results before selecting the anonymous object with the Rank property, but the ordering ends up getting lost. It seems that linq to entities discards the ordering of the separate sets and converts back to natural ordering during both Concat and Union.

What I think I may be able to do is dynamically transform the expression defined in the orderBy variable from x => x.Name to x => x.Entity.Name, but I'm not sure how:

if (orderBy != null)
{
    var transformedExpression = ???
    rankedResults = rankedResults.ThenBy(transformedExpression);
}

How might I be able to use Expression.Lambda to wrap x => x.Name into x => x.Entity.Name? When I hard code x => x.Entity.Name into the ThenBy I get the ordering that I want, but the orderBy is provided by the calling class of the query, so I don't want to hard-code it in. I have it hardcoded in the example above for simplicity of explanation only.

解决方案

This should help. However you are going to have to concrete up the Anonymous type for this to work. My LinqPropertyChain will not work with it, since its going to be difficult to create the Expression<Func<Anonymous, Person>> whilst its still Anonymous.

Expression<Func<Person, object>> orderBy = x => x.Name;

using(var dbContext = new MyDbContext())
{
var keyword = "term";
var startsWithResults = dbContext.People
    .Where(x => x.Name.StartsWith(keyword))
    .Select(x => new {
        Rank = 1,
        Entity = x,
    });
var containsResults = dbContext.People
    .Where(x => !startsWithResults.Select(y => y.Entity.Id).Contains(x.Id))
    .Where(x => x.Name.Contains(keyword))
    .Select(x => new {
        Rank = 2,
        Entity = x,
    });


var rankedResults = startsWithResults.Concat(containsResults)
    .OrderBy(x => x.Rank)
    .ThenBy(LinqPropertyChain.Chain(x => x.Entity, orderBy));

// TODO: apply thenby ordering here based on the orderBy expression above

}

public static class LinqPropertyChain 
{

    public static Expression<Func<TInput, TOutput>> Chain<TInput, TOutput, TIntermediate>(
        Expression<Func<TInput, TIntermediate>> outter,
        Expression<Func<TIntermediate, TOutput>> inner
        )
    {

        Console.WriteLine(inner);
        Console.WriteLine(outter);
        var visitor = new Visitor(new Dictionary<ParameterExpression, Expression>
        {
            {inner.Parameters[0], outter.Body}
        });

        var newBody = visitor.Visit(inner.Body);
        Console.WriteLine(newBody);
        return Expression.Lambda<Func<TInput, TOutput>>(newBody, outter.Parameters);
    }

    private class Visitor : ExpressionVisitor
    {
        private readonly Dictionary<ParameterExpression, Expression> _replacement;

        public Visitor(Dictionary<ParameterExpression, Expression> replacement)
        {
            _replacement = replacement;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (_replacement.ContainsKey(node))
                return _replacement[node];
            else
            {
                return node;
            }
        }
    }
}

Figured out a way to do this with less Explicite Generics.

Expression<Func<Person, object>> orderBy = x => x.Name;
Expression<Func<Foo, Person>> personExpression = x => x.Person;

var helper = new ExpressionChain(personExpression);
var chained = helper.Chain(orderBy).Expression;


// Define other methods and classes here
public class ExpressionChain<TInput, TOutput>
{
    private readonly Expression<Func<TInput, TOutput>> _expression; 
    public ExpressionChain(Expression<Func<TInput, TOutput>> expression)
    {
        _expression = expression;
    }

    public Expression<Func<TInput, TOutput>> Expression { get { return _expression; } }

    public ExpressionChain<TInput, TChained> Chain<TChained>
        (Expression<Func<TOutput, TChained>> chainedExpression)
    {
        var visitor = new Visitor(new Dictionary<ParameterExpression, Expression>
        {
            {_expression.Parameters[0], chainedExpression.Body}
        });
        var lambda = Expression.Lambda<Func<TInput, TOutput>>(newBody, outter.Parameters);
        return new ExpressionChain(lambda);
    }

    private class Visitor : ExpressionVisitor
    {
        private readonly Dictionary<ParameterExpression, Expression> _replacement;

        public Visitor(Dictionary<ParameterExpression, Expression> replacement)
        {
            _replacement = replacement;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (_replacement.ContainsKey(node))
                return _replacement[node];
            else
            {
                return node;
            }
        }
    }
}

这篇关于如何转换这个linq表达式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆