在Entity Framework中的Select for projection中动态传递属性

时间:2017-02-08 13:21:02

标签: c# entity-framework

有没有办法在动态选择中传递我想要从数据库中检索的属性,我事先并不知道我需要的属性,我不想在我的存储库中写入条件。

我不想一次检索所有字段,只根据某些条件检索我需要的字段。

例如:

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();

1 个答案:

答案 0 :(得分:0)

<强>选择

一种简单但丑陋的方式是使用选择器:

var popup = window.open("https://www.google.com", "popup_window", "fullscreen");
  if (popup.outerWidth < screen.availWidth || popup.outerHeight < screen.availHeight)
  {
    popup.moveTo(0,0);
    popup.resizeTo(screen.availWidth, screen.availHeight);
  }

选择器看起来像这样:

query.Select(Selector()).FirstOrDefault();

正如您所看到的,明显的缺点是您需要复制/粘贴所有其他选定的属性。这就是它丑陋的原因。

<强> AutoMapper

  1. <强> CONFIGS

    您可以使用不同配置的AutoMapper。首先,您需要为所有不需要动态的属性定义标准映射。

    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
        };
    }
    

    接下来,您需要定义不同的配置并将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. } 方法添加到每个invidual映射。

    AddStandardStudentMap

    在此之后,只需使用您的条件来决定您需要哪个配置

    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()

    正如我们所看到的,这个解决方案仍然很丑陋并且有很多开销,但在某些情况下需要它,这就是我在这里说的原因。

  2. <强>表达式

    这有点类似于Configs,但为您带来更多灵活性和更少的写作努力。

    首先创建配置,但这次使用选择器

    query.ProjectTo<StudentViewModel>(provider).FirstOrDefault();
    

    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()))
    );
    

    然后就像配置解决方案一样使用它,但不选择特定的配置:

    private static Expression<Func<Student, StudentViewModel>> Selector()
    {
        if(Condition())
            return src => src.Property1;
        else
            return src => src.Property2;
    }
    
  3. <强>结论

    我知道这是很多输入,无论有没有AutoMapper,都有更多可能实现你想要的行为。但我建议你(基于你给我们的信息)使用AutoMapper和Expressions。它应该提供您所需的灵活性,并且可以扩展以满足进一步的要求。