我可以在linq语句中重构范围变量吗?

时间:2010-02-09 23:10:00

标签: c# linq entity-framework

我有一个实体框架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};

ProfessorDAO.cs:

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 个答案:

答案 0 :(得分:2)

简短回答:我担心你很容易做的事情并不多。问题是您需要参数化查询。但是,查询需要表示为表达式树(以便可以将其转换为SQL或EF使用的任何内容)。不幸的是,C#没有提供任何组合表达式树的优雅方法。

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

我将使用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不提供任何内置支持,使其更容易。但是,通过一些努力,您可以编写由表达式树的某些部分参数化的查询。在您的情况下,它使代码更短,更少重复,但它使它更复杂(并且您还需要使用类似我引用的提供AsExpandableExpand的库

在这种情况下,我担心它不值得努力。但是如果C# v(Next)^ x 为这样的事情提供了更优雅的支持,那将是很好的: - )