代码之家  ›  专栏  ›  技术社区  ›  itchi

我可以重构LINQ语句中的范围变量吗?

  •  1
  • itchi  · 技术社区  · 16 年前

    我有一个实体框架3.5项目,我在其中使用TPH继承。我的两个具体类型在它们自己的DAO类中,并且包含一个将实体类投影到DTO中的选择器。但是,这两个具体类与另一个表有类似的关系,我使用let语句来清楚地标识它。我的问题是,我能否以某种方式重构这个let语句,因为当我创建更多继承的具体类时,我觉得我违反了dry。

    StudentDAO.cs:

    var myDTO = from x in context.Students
    let address = (from a in x.addresses where a.active == true select a).FirstOrDefault()
    select new StudentDTO { id = x.id, studentname = x.studentname, address = a.address};
    

    教授:

    var myDTO = from x in context.Professors
    let address = (from a in x.addresses where a.active == true select a).FirstOrDefault()
    select new ProfessorDTO { id = x.id, professorname = x.studentname, address = a.address};
    

    那么,有没有一种方法可以从这两个查询中重构地址呢?

    1 回复  |  直到 8 年前
        1
  •  2
  •   Tomas Petricek    8 年前

    简短回答: 恐怕你没什么办法 容易地 做。问题是您需要参数化查询。但是,需要将查询表示为表达式树(以便可以将其转换为SQL或任何EF使用的类型)。不幸的是,C没有提供任何优雅的方式来组成表达树。

    长回答: 一段时间前,我写了一篇解释 how to compose queries (使用一些技巧)-可以获取表达式树并将其拼接到参数化查询中。因此,原则上,您可以使用本文中的技术创建一个方法,该方法获取表达式树的变化部分,然后组成查询。

    我将使用Linq to SQL中使用的类型,因为我更熟悉它,但我相信原理应该是相同的:

    IQueryable<R> GetInfo<T>(IQueryable<T> source, // e.g. context.Students
      Expression<Func<T, IQueryable<Address>>> getAddr, // x => x.adresses
      Expression<Func<T, Address, R>> getRes // (x, a) => new StudentDTO { ... }
      return
        from x in source.AsExpandable()
        let address = (from a in getAddr(x).Expand() where a.active == true 
                       select a).FirstOrDefault() 
        select getRes(x, a).Expand();
    }
    
    // The use would look like this
    var res = GetInfo(context.Professors, x => x.addresses, (x, a) => new 
      ProfessorDTO { id = x.id, professorname = x.studentname, address = a.address }); 
    

    因此,总结一下,C和Linq不提供任何内置支持,这将使其更容易实现。但是,通过一些努力,您可以编写由表达式树的某些部分参数化的查询。在您的例子中,它使代码稍微短一点,减少了重复性,但是它使代码更加复杂(您还需要使用像我引用的库那样提供 AsExpandable Expand )

    在这种情况下,恐怕这不值得努力。但是如果C# V(下)^ x 为这样的事情提供了一些更优雅的支持:—)