如何使用Entity Framework使用DTO更新数据库中的实体

时间:2016-01-23 10:34:56

标签: c# entity-framework automapper

我有一个三层应用程序。图层是DAL,BL和UI。我正在使用automapper将从数据库上下文中获取的实体转换为上层。据我所知,EF不再能够跟踪映射的实体。

无论如何,我面临的问题发生在更新DB中存在的实体时,更确切地说是DTO实体更新EF实体。来自上层的DTO被映射回实体以完成更新操作。但是,从DTO实体映射并且其所有导航属性和集合在上下文中不存在,因为它们未被跟踪。

EF接受此实体及其实体图作为新信息,而不考虑该实体及其中包含的某些实体已经存在于上下文中的事实。

这里有一些例子。

我有两个模型,StudentStandard

public class Student
{
    [Key]
    public Guid StudentID { get; set; }
    public string StudentName { get; set; }
    public DateTime DateOfBirth { get; set; }
    public decimal Height { get; set; }
    public float Weight { get; set; }

    public Standard Standard { get; set; }
}

public class Standard
{
    [Key]
    public Guid StandardId { get; set; }
    public string StandardName { get; set; }

    public ICollection<Student> Students { get; set; }
}

我的背景:

public class SchoolContext : DbContext
{
    public DbSet<Student> Students { get; set; }
    public DbSet<Standard> Standards { get; set;}
}

我的StudentMapStandardMap看起来与上面提到的模型相同。

自动播放器配置文件:

public class AutomapperProfile : Profile
{
    protected override void Configure()
    {
        Mapper.CreateMap<Student, StudentMap>();
        Mapper.CreateMap<StudentMap, Student>();
        Mapper.CreateMap<Standard, StandardMap>();
        Mapper.CreateMap<StandardMap, Standard>();
    }
}

这里是我运行的代码:

 Mapper.AddProfile(new AutomapperProfile());

 var student1 = new Student
                          {
                              DateOfBirth = DateTime.Now,
                              Height = 195,
                              StudentID = Guid.NewGuid(),
                              StudentName = "Bob",
                              Weight = 144
                          };

var student2 = new Student
                           {
                               DateOfBirth = DateTime.UtcNow,
                               Height = 170,
                               StudentID = Guid.NewGuid(),
                               StudentName = "John",
                               Weight = 95,
                           };

var standard = new Standard
                           {
                               StandardId = Guid.NewGuid(),
                               StandardName = "New Standard",
                               Students = new List<Student> { student1 }
                           };

using (var schoolContext = new SchoolContext())
{
    schoolContext.Standards.Add(standard);
    schoolContext.Students.Add(student2);

    schoolContext.SaveChanges();

    var standardInContext = schoolContext.Standards.First();
    var studentInContext = schoolContext.Students.First(student => student.StudentID == student2.StudentID);

    var mappedStandad = Mapper.Map<Standard, StandardMap>(standardInContext);
    var mappedStudent = Mapper.Map<Student, StudentMap>(studentInContext);
    mappedStandad.Students.Add(mappedStudent);

    var standardEf = Mapper.Map<StandardMap, Standard>(mappedStandad);
    //On attach attempt exception will be thrown
    //because standard with the same Id already exist in context
    schoolContext.Set<Standard>().Attach(standardEf);
    schoolContext.Entry(standardEf).State = EntityState.Modified;

    // with this solution two additional student will be added to the context, 
    // exception will be thrown on SaveChanges, because students with 
    // same ID already exist
    // var standardEf = Mapper.Map(mappedStandad, standardInContext);
    // schoolContext.Set<Standard>().Attach(standardEf);
    // schoolContext.Entry(standardEf).State = EntityState.Modified;
    schoolContext.SaveChanges();
}

我会感谢任何帮助!我已经检查了我能做什么但是徒劳无功。

2 个答案:

答案 0 :(得分:3)

Standard提供了例外情况,因为您已添加StandardID个实体schoolContext.Standards.Add(standard);之前Standard

解决方案:

  1. StandardMap.StandardID

  2. 从上下文中请求StandardMap个实体
  3. Standard转移对象的属性值映射到请求的Mapper.Map<Source, Destination>(source, destination);实体(手动或使用Mapper.CreateMap<StudendMap, Student>. ForMember( x => x.Standard, m => m.ResolveUsing( s => Context.Set<Standard>.Find(s.StandardID)))

  4. 保存更改

  5. ADDED

    如果您想使用AutoMapper,您必须正确配置mapper以从上下文请求嵌套实体,而不是创建它们,例如:

    library(ggplot2)
    library(XML)
    tab <- readHTMLTable("http://www.50states.com/abbreviations.htm", which=1)
    df$region <- tolower(tab[match(df$State, tab[, 2]), 1])
    states <- map_data("state")
    choro <- merge(states, df, sort = FALSE, by = "region")
    choro <- choro[order(choro$order), ]
    ggplot(choro, aes(long, lat.x)) +
      geom_polygon(aes(group = group, fill = Count), colour="grey55") +
      coord_quickmap() + 
      facet_wrap(~Year)
    

答案 1 :(得分:0)

附加完整图表是一项复杂的任务。在这种情况下,EF将Student实例附加为已添加,这是将对象附加到集合时的行为。

为避免招收新生,您应首先将所有标准学生附加到上下文中,以便开始跟踪(未经修改),然后附加standardEf实体。