有没有一种方法可以在Select中动态传递我想从DB中检索的属性,我事先不知道我需要的属性,也不想在我的存储库中写入条件。
我不想一次检索所有字段,只想根据某些条件检索我需要的字段。
例如:
public class Student
{
public string Property1 {get; set;}
public string Property2 {get; set;}
//other properties here
[NotMapped]
public string Selected
{
if(condition)
return Property1;
else
return Property2;
}
}
在服务层我有
query.Select(s => new StudentViewModel
{
Value = s.Selected; //this one will get the property we want based on a condition
//other stuff here
//OtherValue = s.OtherProperty
}
).FirstOrDefault();
选择器
一个简单但丑陋的方法是使用Selector:
query.Select(Selector()).FirstOrDefault();
选择器可以是这样的:
private static Expression<Func<Student, StudentViewModel>> Selector()
{
if (Condition())
return x => new StudentViewModel
{
Name = x.Property1,
OtherName = x.OtherName
};
return x => new StudentViewModel
{
Name = x.Property2,
OtherName = x.OtherName
};
}
正如您所看到的,这里明显的缺点是您需要复制/粘贴所有其他选定的属性。这就是它丑陋的原因。
自动映射器
配置
您可以将AutoMapper与不同的配置一起使用。首先,您需要为所有不需要动态的属性定义一个标准映射。
public static void AddStandardStudentMap(this IMappingExpression<Student, StudentViewModel> map) { map.ForMember(dest => dest.OtherName, opt => opt.MapFrom(src => src.OtherProperty)) .ForMember(dest => dest.OtherName2, opt => opt.MapFrom(src => src.OtherProperty2)); // you can concat .ForMember() for each property you need. }
接下来,您需要定义不同的配置,并将
AddStandardStudentMap
方法添加到每个invidual映射中。var config1 = new MapperConfiguration(cfg => cfg.CreateMap<Student, StudentViewModel>() .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Property1)) .AddStandardStudentMap() ); var config2 = new MapperConfiguration(cfg => cfg.CreateMap<Student, StudentViewModel>() .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Property2)) .AddStandardStudentMap() );
在这之后,只需使用您的条件来决定您需要的配置
IConfigurationProvider provider; if(Condition()) provider = config1; else provider = config2;
然后使用代替
.Select()
query.ProjectTo<StudentViewModel>(provider).FirstOrDefault();
正如我们所看到的,这个解决方案仍然很丑陋,有很多开销,但在某些情况下是需要的,这就是我在这里说的原因。
表达式
这与Configs有点相似,但为您带来了更大的灵活性和更少的编写工作量。
首先创建一个配置,但这次使用Selector
var config = new MapperConfiguration(cfg => cfg.CreateMap<Student, StudentViewModel>() .ForMember(dest => dest.OtherName, opt => opt.MapFrom(src => src.OtherName)) .ForMember(dest => dest.OtherName2, opt => opt.MapFrom(src => src.OtherName2)) // and so on. Map all your properties that are not dynamically. // and then the Selector .ForMember(dest => dest.Name, opt => opt.MapFrom(src => Selector())) );
Selector方法可以如下所示:
private static Expression<Func<Student, StudentViewModel>> Selector() { if(Condition()) return src => src.Property1; else return src => src.Property2; }
然后像使用configs解决方案一样使用它,但不选择特定的配置:
query.ProjectTo<StudentViewModel>(config).FirstOrDefault();
结论
我知道这是一个很大的投入,无论有没有AutoMapper,都有更多的可能性来实现你想要的行为。但我建议您(根据您提供的信息)使用带有表达式的AutoMapper。它应该提供您需要的灵活性,并且可以扩展以满足进一步的需求。