我们的项目中有很多Dto类,并且在不同的场合使用来自实体框架上下文的表达式来选择它们。这样做的好处是,EF可以解析我们的请求,并从中构建一个很好的SQL语句。
不幸的是,这导致了非常大的表达,因为我们无法将它们组合起来。
因此,如果您有一个具有3个属性的类DtoA,并且其中一个属于具有5个属性的DtoB类,并且其中一个属于具有10个属性的DtoC类,则必须编写一个大选择器。
public static Expression<Func<ClassA, DtoA>> ToDto =
from => new DtoA
{
Id = from.Id,
Name = from.Name,
Size = from.Size,
MyB = new DtoB
{
Id = from.MyB.Id,
...
MyCList = from.MyCList.Select(myC => new DtoC
{
Id = myC.Id,
...
}
}
};
此外,它们无法重复使用。如果你有DtoD,它也具有DtoB类的属性,你将不得不再次粘贴所需的DtoB和DtoC代码。
public static Expression<Func<ClassD, DtoD>> ToDto =
from => new DtoD
{
Id = from.Id,
Length = from.Length,
MyB = new DtoB
{
Id = from.MyB.Id,
...
MyCList = from.MyCList.Select(myC => new DtoC
{
Id = myC.Id,
...
}
}
};
所以这会很快升级。请注意,提到的代码只是一个例子,但你明白了。
我想为每个类定义一个表达式,然后根据需要组合它们,以及EF仍然能够解析它并生成SQL语句,以免失去性能提升。
我怎样才能实现这个目标?
答案 0 :(得分:1)
我想了一下,我没有想出任何&#34;真棒&#34;溶液
基本上你在这里有两个一般的选择,
像这样,
public static Expression<Func<ClassA, DtoA>> DtoExpression{
get{
Expression<Func<ClassA, DtoA>> dtoExpression = classA => new DtoA(){
BDto = Magic.Swap(ClassB.DtoExpression),
};
// todo; here you have access to dtoExpression,
// you need to use expression transformers
// in order to find & replace the Magic.Swap(..) call with the
// actual Expression code(NewExpression),
// Rewriting the expression tree is no easy task,
// but EF will be able to understand it this way.
// the code will be quite tricky, but can be solved
// within ~50-100 lines of code, I expect.
// For that, see ExpressionVisitor.
// As ExpressionVisitor detects the usage of Magic.Swap,
// it has to check the actual expression(ClassB.DtoExpression),
// and rebuild it as MemberInitExpression & NewExpression,
// and the bindings have to be mapped to correct places.
return Magic.Rebuild(dtoExpression);
}
另一种方法是仅开始使用Expression
类(抛弃LINQ)。通过这种方式,您可以从零开始编写查询,并且可重用性会很好,但事情会变得更加困难。你失去了类型安全。 Microsoft对动态表达式有很好的参考。如果以这种方式构建所有内容,则可以重用许多功能。例如,您定义NewExpression
,然后如果需要,您可以稍后重复使用它。
第三种方法是基本上使用lambda语法:.Where
,.Select
等。这样可以提供更好的&#34;可重用性&#34;率。它没有100%解决您的问题,但它可以帮助您更好地撰写查询。例如:from.MyCList.Select(dtoCSelector)
答案 1 :(得分:0)
您是否考虑过使用Automapper?你可以定义你的Dtos并在原始实体和Dto之间创建一个映射和/或反之亦然,并且使用投影,你不需要任何select语句,因为Automapper会自动为你做这个并且它只会投影dto的属性进入SQL查询。
例如,如果您有一个具有以下结构的Person表:
public class Person
{
public int Id { get; set; }
public string Title { get; set; }
public string FamilyName { get; set; }
public string GivenName { get; set; }
public string Initial { get; set; }
public string PreferredName { get; set; }
public string FormerTitle { get; set; }
public string FormerFamilyName { get; set; }
public string FormerGivenName { get; set; }
}
你的dto是这样的:
public class PersonDto
{
public int Id { get; set; }
public string Title { get; set; }
public string FamilyName { get; set; }
public string GivenName { get; set; }
}
你可以在这个
之间创建Person和PersonDto之间的映射 Mapper.CreateMap<Person, PersonDto>()
当您使用Entity Framework查询数据库时(例如),您可以使用类似这样的内容来获取PersonDto列:
ctx.People.Where(p=> p.FamilyName.Contains("John"))
.Project()
.To<PersonDto>()
.ToList();
将返回一个PersonDtos列表,其姓氏包含“John”,如果您运行sql profiler,您将看到只选择了PersonDto列。
Automapper也支持层次结构,如果你的Person有一个链接到它的地址,你想为它返回AddressDto。
我认为值得一看并检查它,它清除了手动映射所需的许多混乱。