如何在EF4.1纯代码场景中重用部分Linq-to-EntitySelect表达式

本文关键字:用部 表达式 Linq-to-EntitySelect EF4 代码 | 更新日期: 2023-09-27 17:58:52

以下是我拥有的代码的简化版本:

class PrintJob : IEntity
{
    public string UserName { get; set; }
    public string Departmen { get; set; }
    public int PagesPrinted { get; set; }
}
class PrintJobReportItem
{
    public int TotalPagesPrinted { get; set; }
    public int AveragePagesPrinted { get; set; }
    public int PercentOfSinglePagePrintJobs { get; set; }
}
class PrintJobByUserReportItem : PrintJobReportItem
{
    public string UserName { get; set; }
}
class PrintJobByDepartmenReportItem : PrintJobReportItem
{
    public string DepartmentName { get; set; }
    public int NumberOfUsers { get; set; }
}

然后我有两个问题:

var repo = new Repository(...);
var q1 = repo.GetQuery<PrintJob>()
    .GroupBy(pj => pj.UserName)
    .Select(g => new PrintJobByUserReportItem
    {
    #region this is PrintJobReportItem properties
        TotalPagesPrinted = g.Sum(p => p.PagesPrinted),
        AveragePagesPrinted = g.Average(p => p.PagesPrinted),
        PercentOfSinglePagePrintJobs = g.Count(p => p.PagesPrinted == 1) / (g.Count(p => p.PagesPrinted) != 0 ? g.Count(p => p.PagesPrinted) : 1) * 100,
    #endregion    
        UserName = g.Key
    });
var q2 = repo.GetQuery<PrintJob>()
    .GroupBy(pj => pj.Departmen)
    .Select(g => new PrintJobByDepartmenReportItem
    {
    #region this is PrintJobReportItem properties
        TotalPagesPrinted = g.Sum(p => p.PagesPrinted),
        AveragePagesPrinted = g.Average(p => p.PagesPrinted),
        PercentOfSinglePagePrintJobs = g.Count(p => p.PagesPrinted == 1) / (g.Count(p => p.PagesPrinted) != 0 ? g.Count(p => p.PagesPrinted) : 1) * 100,
    #endregion    
        DepartmentName = g.Key,
        NumberOfUsers = g.Select(u => u.UserName).Distinct().Count()
    });

对于从这两个查询中提取我为TotalPagesPrinted、AveragePagesPrintedandPercentOfSinglePagePrintJobs赋值的部分,有什么建议,以便它可以重复使用并遵循DRY原则。

我使用的是仅EF 4.1代码的方法,而切换到另一种技术或方法不是一种选择。此外,我无法实现该数据,我需要将其保留为查询,因为我的网格组件稍后会添加更多的内容进行查询,所以我无法切换到Linq到Object。

如何在EF4.1纯代码场景中重用部分Linq-to-EntitySelect表达式

我将创建一个具有两个属性的新类CLASSNAME

  • PrintJobReportItem类型
  • 分组IEnumerable<IGrouping<TKey, TSource>>

然后创建一个扩展方法

public static IQueryable<CLASSNAME> EXTENSIONNAME<TKey, TSource>(this IEnumerable<IGrouping<TKey, TSource>> source)
{
  return from g in source
         select new CLASSNAME
         {
           PrintJobReportItem = new PrintJobReportItem
                                {
                                  TotalPagesPrinted = g.Sum(p => p.PagesPrinted),
                                  AveragePagesPrinted = etc...,
                                  PercentOfSinglePagePrintJobs = etc...,
                                },
           GROUPING = g
         };
}

然后像这样使用,我还没有测试,但我认为它会工作

var q1 = repo.GetQuery<PrintJob>()
    .GroupBy(pj => pj.UserName)
    .EXTENSIONNAME()
    .Select(g => new PrintJobByDepartmenReportItem
                 {
                    PrintJobReportItem = g.PrintJobReportItem,
                    DepartmentName = g.GROUPING.Key,
                    NumberOfUsers = g.GROUPING.Select(u => u.UserName).Distinct().Count()
                 });

我能想到的最简单的事情是创建一个接受单个IEnumerable<IGrouping<string, PrintJob>>参数的PrintJobByDepartmenReportItem构造函数(我认为它应该是示例中变量g的类型)。请记住,这还需要一个无参数的构造函数定义,并且您继承的类还需要实现一个构造函数原型来调用带有参数的基类构造函数:

构造函数

public PrintJobReportItem()
{
}
public PrintJobReportItem(IEnumerable<IGrouping<string, PrintJob>> g)
{
    this.TotalPagesPrinted = g.Sum(i => i.GetEnumerator().Current.PagesPrinted);
    this.AveragePagesPrinted = g.Average(i => i.GetEnumerator().Current.PagesPrinted);
    this.PercentOfSinglePagePrintJobs = g.Count(i => i.GetEnumerator().Current.PagesPrinted == 1) * 100 / g.Count(i => i.GetEnumerator().Current.PagesPrinted > 1);
}

继承的构造函数

public PrintJobByDepartmentReportItem(IEnumerable<IGrouping<string, PrintJob>> g) : base(g)
{
    this.DepartmentName = g.First().Key;
    this.NumberOfUsers = g.Select(i => i.GetEnumerator().Current.UserName).Distinct().Count();
}

查询

var q1 = repo.GetQuery<PrintJob>()
    .GroupBy(pj => pj.UserName)
    .Select(g => new PrintJobByUserReportItem(g));
var q2 = repo.GetQuery<PrintJob>()
    .GroupBy(pj => pj.Department)
    .Select(g => new PrintJobByDepartmentReportItem(g));

这确实有一个缺点,即假设您总是按字符串成员进行分组,但您可以在适当的时候推测为GroupBy(i => i.MyProperty.ToString()),或者可能更改原型以接受IEnumerable<IGrouping<object, PrintJob>>