【问题标题】:Unable to Map Complex Entity Framework Core using AutoMapper无法使用 AutoMapper 映射复杂的实体框架核心
【发布时间】:2021-07-26 06:49:57
【问题描述】:

我正在努力弄清楚如何使用 AutoMapper 正确地将具有继承的 EF 核心对象映射到 DTO。我正在寻找的正确结果是一个 DTO,它有一个培训和所有为其注册的员工:

以下是三个类:

public class Training
{
     public Guid Id { get; set; }
     public string Host { get; set; }
     public string Name { get; set; }

     public ICollection<EmployeeTraining> EmployeeTrainings { get; }
}

public class Employee
{
    public Guid Id { get; set; }
    public string Designation { get; set; }

    public ICollection<EmployeeTraining> EmployeeTrainings { get; }
}

public class EmployeeTraining    
{
    public Guid EmployeeId {get; set;}
    public Employee Employee { get; set; }

    public Guid TrainingId { get; set; }
    public Training Training{ get; set; }
}

public class TrainingDTO
{
     public string Host { get; set; }
     public string Name { get; set; }

     public ICollection<EmployeeTraining> EmployeeTrainings { get; }
}

AutoMapper 配置文件:

public class TrainingProfile: Profile
{
    public TrainingProfile()
    {
        CreateMap<Training, TrainingDto>()
            .ForMember(entity => entity.EmployeeTrainings, opt => opt.MapFrom(model => model));
        CreateMap<TrainingDto, Training>();
    }
}

代码编译,但运行时我得到:

[2021-05-03T21:21:49.201Z] Exception: Error mapping types.
[2021-05-03T21:21:49.203Z] Mapping types:
[2021-05-03T21:21:49.204Z] Training -> TrainingDto
[2021-05-03T21:21:49.208Z] AzITSSMaster.API.Entities.Training -> AzITSSMaster.API.Models.TrainingDto
[2021-05-03T21:21:49.209Z] Type Map configuration:
[2021-05-03T21:21:49.209Z] Training -> TrainingDto
[2021-05-03T21:21:49.211Z] AzITSSMaster.API.Entities.Training -> AzITSSMaster.API.Models.TrainingDto
[2021-05-03T21:21:49.212Z] Destination Member:
[2021-05-03T21:21:49.212Z] EmployeeTrainings

测试

    [Fact]
    public void Test1()
    {
        Guid trainingOneGuid = new Guid("5b1c2b4e48c7402a80c3cc796ad49c6b");
        Guid trainingTwoGuid = new Guid("d8663e5f74944f8187396e0de1bea7ee");
        Guid employeeOneGuid = new Guid("5b1c2b4d48c7402a80c3cc796ad49c6b");
        Guid employeeTwoGuid = new Guid("d8663e5e74944f8187396e0de1bea7ee");

        //Create In Memory Database
        var options = new DbContextOptionsBuilder<ITSSAPIContext>()
        .UseInMemoryDatabase(databaseName: "ITSSMaster")
        .Options;

        using (var context = new ITSSAPIContext(options))
        {
            TrainingRepository trepo = new TrainingRepository(context);
            EmployeeRepository erepo = new EmployeeRepository(context);
            EmployeeTrainingRepository etrepo = new EmployeeTrainingRepository(context);

            // Create
            trepo.Create(new Training
            {
                Id = trainingOneGuid,
                Host = "ATS",
                Name = "ATS Test Training 1",
                StartDate = DateTime.Now,
                Facillitator = "Darth Maul",
                StipendAmount = 750,

            });

            trepo.Create(new Training
            {
                Id = trainingTwoGuid,
                Host = "CO",
                Name = "CO Test Training 1",
                StartDate = DateTime.Now,
                Facillitator = "Darth Vader",
                StipendAmount = 0
            });

            erepo.Create(new Employee
            {
                Id = Guid.Parse("5b1c2b4d-48c7-402a-80c3-cc796ad49c6b"),
                OrgDefinedId = 000032615,
                Designation = String.Empty
            });

            erepo.Create(new Employee
            {
                Id = Guid.Parse("d8663e5e-7494-4f81-8739-6e0de1bea7ee"),
                OrgDefinedId = 000012345,
                Designation = "Champion"
            });

            //Training: GetIncluding
            Training training1 = trepo.GetIncluding<Training>(
                trainingOneGuid,
                c => c.EmployeeTrainings);
            Assert.Equal("ATS Test Training 1", training1.Name);

            Training training2 = trepo.GetIncluding<Training>(
                trainingTwoGuid,
                c => c.EmployeeTrainings);
            Assert.Equal("CO Test Training 1", training2.Name);

            //Employee: GetIncluding
            Employee employee1 = erepo.GetIncluding<Employee>(
                employeeOneGuid,
                c => c.EmployeeTrainings);
            Assert.Equal(000032615, employee1.OrgDefinedId);

            Employee employee2 = erepo.GetIncluding<Employee>(
            employeeTwoGuid,
            c => c.EmployeeTrainings);
            Assert.Equal(000012345, employee2.OrgDefinedId);

            //EmployeeTraining:  Add
            etrepo.AddTrainingToEmployee(employee1, training1);
            etrepo.AddTrainingToEmployee(employee2, training1);
            Assert.Equal(2, training1.EmployeeTrainings.Count);

            etrepo.AddTrainingToEmployee(employee1, training2);
            Assert.Equal(1, training2.EmployeeTrainings.Count);

            Assert.Equal(2, employee1.EmployeeTrainings.Count);
            Assert.Equal(1, employee2.EmployeeTrainings.Count);

            //Automapper
            var config1 = new MapperConfiguration(cfg =>
            {
                cfg.CreateMap<Training, TrainingDto>()
                    .ForMember(dst => dst.EmployeeTrainings, 
                    opt => opt.MapFrom(model => model.EmployeeTrainings));
            });

            //Using automapper
            var mapper1 = new Mapper(config1);
            var TrainingDto1 = mapper1.Map<TrainingDto>(training1);
            var TrainingDto2 = mapper1.Map<TrainingDto>(training2);
        }
    }

观察窗口

【问题讨论】:

    标签: c# entity-framework-core automapper


    【解决方案1】:

    首先,通过在应用程序启动时调用 IServiceCollection 扩展方法 AddAutoMapper 来检查您是否已注册您的个人资料:

    services.AddAutoMapper();
    

    然后,将您的 EmployeeTrainings 分配稍微更改为:

        CreateMap<Training, TrainingDto>()
            .ForMember(dst => dst.EmployeeTrainings, opt => opt.MapFrom(model => model.EmployeeTrainings));
    

    【讨论】:

    • 继承没有按预期工作(DTO 的 EmployeeTraining 字段为空) 我添加了我的测试代码和结果(上面的观察窗口)。感谢您查看此问题 - 我已经尝试解决此问题两天了。
    • 我看不出继承与您的方案之间有任何联系。你所有的类都继承自 object
    • 抱歉 - 我有时会混淆术语。我要做的是将属于培训对象的所有员工映射到培训 DTO,以便在将培训数据返回给用户时将这些员工与培训一起返回。
    • 我真的认为继续让 DTO 对象引用 EmployeeTraining 类是一个糟糕的选择。我会鼓励你创建一个 EmployeeTrainingDto。为什么?由于导航属性:在映射之前,EmployeeTraining.Training 引用的是 Training 对象。在映射之后,您希望此引用会发生什么?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-30
    • 2020-10-15
    • 1970-01-01
    • 2012-04-19
    • 2012-12-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多