如何在Linq(到EF或SQL)中组合多个重构的Select表达式?

时间:2010-11-15 11:28:06

标签: linq linq-to-entities

说我有这个视图模型:

public class SeriesLinkViewModel
{
    public static Expression<Func<Series, SeriesLinkViewModel>> FromSeries =
        s => new SeriesLinkViewModel
        {
            Name = s.Name,
            Slug = s.Slug,
        };

    public string Name { get; set; }
    public string Slug { get; set; }
}
为了方便起见,我把投影功能放在那里,所以现在我可以这样说:

var links = dc.Series.Select(SeriesLinkViewModel.FromSeries);

真棒。但是,如果我想添加到此查询,该怎么办?假设我还要从表中提取Description列。通常情况下,我可以做一个select new { }并将Description放在那里,但我不能那样做,因为我只能在`.Select()中添加一个投影函数。

我希望我能做到这样的事情:

q = from s in dc.Series
    select new
    {
        Series = SeriesLinkViewModel.FromSeries.Compile()(s),
        Description = s.Description
    };

但我得到一个例外:

  

System.InvalidCastException:无法执行   铸造对象的类型   'System.Linq.Expressions.FieldExpression'   输入   'System.Linq.Expressions.LambdaExpression'。

或者我可以至少在一次往返中以某种方式完成所有这些查询?我知道TransactionScope可以进行更改,但我认为它不会导致查询一次完成。

4 个答案:

答案 0 :(得分:6)

LinqKit

解决了这个问题
var fs = SeriesLinkViewModel.FromSeries; //needs to be local for some reason
q = from s in dc.Series.AsExpandable() //enables LinqKit to do its magic
    select new
    {
        Series = fs.Invoke(s), //and voila!
        Description = s.Description
    };

答案 1 :(得分:2)

这是Rei答案的补充(我标记为正确)。如果要删除对局部变量的依赖性,请查看this answer from Dan Abramov on how to fix LinqKit

答案 2 :(得分:0)

我知道这不是你想要的,但一个可能的解决方法是创建一个像这样的方法

private IQueryable<SeriesLinkViewModel> FromSeries(IQueryable<Series> seriesQuery)
{
    return from s in seriesQuery
           select new SeriesLinkViewModel
           {
                Name = s.Name,
                Slug = s.Slug
           };
}

然后,当您想要使用投影时,请通过它运行查询。

return FromSeries(from s in Series
                  where s.Name == "foo"
                  select s);

不理想,因为你没有创建一个可以与其他人结合的可重用表达式,但至少你只有一个映射函数可以运行所有类似的查询。

答案 3 :(得分:0)

在我看来,这是一个更好的解决方案。

public class SeriesLinkViewModel
{
    public static Expression<Func<Series, SeriesLinkViewModel>> FromSeries =
        s => new SeriesLinkViewModel
        {
            Name = s.Name,
            Slug = s.Slug,
        };

    public string Name { get; set; }
    public string Slug { get; set; }
}

public class SeriesLinkExtendedViewModel: SeriesLinkViewModel
{
    public new static Expression<Func<Series, SeriesLinkExtendedViewModel>> FromSeries = 
        SeriesLinkViewModel.FromSeries.Merge(s => new SeriesLinkExtendedViewModel
        {
            Description = s.Description
        });

    public string Description { get; set; }
}

// Somewhere else...
var q = from s in dc.Series.Select(SeriesLinkExtendedViewModel.FromSeries);

“合并”扩展方法将返回一个投影,该投影是合并两个投影表达式的结果,因此包含三个列:Name,Slug和Description。

可以在以下链接中找到实际的实施:https://coding.abel.nu/2013/01/merging-expression-trees-to-reuse-in-linq-queries/