【问题标题】:AutoMapper.ProjectTo with field level type conversionAutoMapper.ProjectTo 具有字段级类型转换
【发布时间】:2016-05-12 18:59:41
【问题描述】:

我正在尝试使用 AutoMapper 的 ProjectTo 扩展方法将基于实体框架表的对象转换为自定义 DTO。 EF 端的 Person 实体如下所示:

public partial class Person
{
    public int PersonId { get; set; }
    public string Name { get; set; }
    public string Age { get; set; }
}

我投影到的 PersonViewModel DTO 如下所示:

public class PersonViewModel
{
    public PersonViewModel(Person p)
    {
        PersonId = p.PersonId;
        Name = p.Name;
        Age = Convert.ToInt32(p.Age);
    }

    public PersonViewModel()
    {

    }

    public PersonViewModel(int id, string name, int age)
    {
        PersonId = id;
        Name = name;
        Age = age;
    }

    public int PersonId { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

要注意的关键是,person 表包含一个名为 Age 的字段,它是一个 nvarchar 类型,而 PersonViewModel 的 Age 有一个 int 类型。不幸的是,我无法更改表格,必须找到转换数据的方法。

以下代码是我尝试过的 AutoMapper 配置、它们的“问题”以及使用代码(数据库调用等)。

MapperConfiguration config = new MapperConfiguration(cfg => 
        {
            // -----  Throws "Unable to create map expression" exception  ------
            //cfg.CreateMap<Person, PersonViewModel>();


            // -----  Throws "Linq to Entities does not support method" exception  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ForMember(dest => dest.Age, opt => opt.MapFrom(src => Convert.ToInt32(src.Age)));


            // -----  Throws "Unable to create map expression" exception  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ConstructProjectionUsing(src => new PersonViewModel
            //    {
            //        PersonId = src.PersonId,
            //        Name = src.Name,
            //        Age = Convert.ToInt32(src.Age)
            //    });


            // -----  Throws "Unable to create map expression" exception  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ConstructProjectionUsing(src => new PersonViewModel(src.PersonId, src.Name, Convert.ToInt32(src.Age)));


            // -----  Throws "Unable to create map expression" exception  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ConstructProjectionUsing(src => new PersonViewModel(src.PersonId, src.Name, 25));


            // -----  Throws "Linq to Entities does not support method" exception  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ProjectUsing(src => new PersonViewModel
            //    {
            //        PersonId = src.PersonId,
            //        Name = src.Name,
            //        Age = Convert.ToInt32(src.Age)
            //    });


            // -----  Throws "Unable to create map expression" exception  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ConstructUsing(src => new PersonViewModel
            //    {
            //        PersonId = src.PersonId,
            //        Name = src.Name,
            //        Age = Convert.ToInt32(src.Age)
            //    });


            // -----  Throws "Unable to create map expression" exception  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ConstructUsing(src => new PersonViewModel(src));


            // -----  Doesn't throw exception, but all values are null or 0.  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ConvertUsing(src => new PersonViewModel
            //    {
            //        PersonId = src.PersonId,
            //        Name = src.Name,
            //        Age = Convert.ToInt32(src.Age)
            //    });


            // -----  Doesn't throw exception, but all values are null or 0.  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ConvertUsing(src => new PersonViewModel(src.PersonId, src.Name, Convert.ToInt32(src.Age)));


            // -----  Works, but bypasses the conversion I need  ------
            //cfg.CreateMap<Person, PersonViewModel>()
            //    .ProjectUsing(src => new PersonViewModel
            //    {
            //        PersonId = src.PersonId,
            //        Name = src.Name,
            //        Age = 25
            //    });


            // -----  Works, but bypasses the conversion I need  ------
            cfg.CreateMap<Person, PersonViewModel>()
                .ForMember(dest => dest.Age, opt => opt.MapFrom(src => 35));
        });

        using (FamilyEntities db = new FamilyEntities())
        {
            PersonViewModel p = db.Set<Person>()
                .Where(x => x.PersonId == 1)
                .ProjectTo<PersonViewModel>(config)
                .SingleOrDefault();

            Console.WriteLine("Name: " + p.Name + Environment.NewLine + "Age: " + p.Age);
        }

        Console.ReadLine();

是否可以使用 AutoMapper 的 ProjectTo 将字符串转换为 int?其次,如果可能的话,那个配置是​​什么样的?

提前感谢您的帮助。

【问题讨论】:

  • stackoverflow.com/a/24027242/1736944 可能会为您提供一个可行的解决方法,除非 automapper 有一些聪明的东西。
  • @9Rune5 - 感谢您指出 EF 的这个可扩展点。我不知道您可以像这样定义自己的方法。我已经对此进行了测试,这是一个可行的解决方法。如果你提供这个作为答案,我会接受。

标签: c# entity-framework entity automapper projection


【解决方案1】:

您的第一次尝试就是为什么这不起作用。 Entity Framework 不知道如何将字符串转换为 int,所以你很烦。其他方式都不重要,重要的是 EF 给你的例外。

如果 EF 不支持转换,AutoMapper 也无能为力。

相反,我会在您的视图模型中进行转换:

public class PersonViewModel
{
    public int PersonId { get; set; }
    public string Name { get; set; }
    public string Age { get; set; }
    public int AgeValue => Convert.ToInt32(Age);
} 

【讨论】:

  • 感谢吉米的回复。您提供的解决方案肯定会起作用,但它会迫使我在我的模型中添加更多数据(我知道可以忽略不计)、逻辑(尽管微不足道)和冗余(即..Person2ViewModel)。我很惊讶 EF 本身不支持这一点,因为考虑到 TSQL 已经支持 CAST 和 CONVERT,这似乎不是一个疯狂的请求。我认为 9Rune5 为 EF 提供自定义函数的答案对于我的情况来说是一种更好的方法,因为它消除了上面列出的负面因素,并且更符合存在的其余映射/转换。
【解决方案2】:

https://stackoverflow.com/a/24027242/1736944 解释了如何向 EF 添加功能。基本上生成的查询必须执行类型转换。

【讨论】:

    猜你喜欢
    • 2016-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-26
    • 1970-01-01
    相关资源
    最近更新 更多