【问题标题】:How can I combine multiple refactored Select expressions in Linq (to EF or SQL)?如何在 Linq 中组合多个重构的 Select 表达式(到 EF 或 SQL)?
【发布时间】:2010-11-15 11:28:05
【问题描述】:

假设我有这个视图模型:

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 可用于进行更改,但我认为它不会导致查询一次完成。

【问题讨论】:

  • 我不确定您遇到了什么问题。为什么不将Description 列添加到SeriesLinkViewModel 或创建一个包含Description 的新ViewModel 类?
  • 这是一个简单的例子。问题是当我有更多的查询组合要做时。然后我可以进行非常粗略的查询来检索大量的东西,或者我拥有大量的 ViewModel,除了我无法编写精明的查询之外没有什么特别好的理由。

标签: linq linq-to-entities


【解决方案1】:

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
    };

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      我知道这不是您正在寻找的,但一种可能的解决方法是创建这样的方法

      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);
      

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

      【讨论】:

      • 我想我遗漏了一些东西——这与仅仅将表达式传递给 .Select() 有何不同?
      • 我认为它也不应该有所不同....但是 linq 实现中的某些东西意味着它不能将投影表达式合并到查询中。
      【解决方案4】:

      我认为这是一个更好的解决方案。

      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);
      

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

      实际实现可以看这个链接:https://coding.abel.nu/2013/01/merging-expression-trees-to-reuse-in-linq-queries/

      【讨论】:

        猜你喜欢
        • 2018-08-10
        • 2021-10-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多