2016-01-23 175 views
2

我有一個三層應用程序。這些圖層是DAL,BL和UI。我使用automapper將從數據庫上下文中取得的實體轉換爲上層圖層。據我所知,被映射的實體不能被EF跟蹤。如何使用實體框架使用DTO更新數據庫中的實體

無論如何,我面臨的問題發生在更新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看起來一樣上面提到的車型。

Automapper簡介:

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

我會感謝您的幫助!我已經檢查了我所能做的,但是徒勞無功。

回答

0

附加完整圖是一項複雜的任務。在這種情況下,EF將Student實例作爲Added添加,這是將對象附加到集合時的行爲。

爲避免招收新生,您應首先將所有標準的學生附加到上下文中,以便開始跟蹤(如未修改),然後附加standardEf實體。

3

schoolContext.Set<Standard>().Attach(standardEf);給出了例外,因爲您已經添加了Standard實體與之前的StandardID相同schoolContext.Standards.Add(standard);

解決辦法:

  1. 請求從上下文Standard實體通過StandardMap.StandardID

  2. 地圖屬性值從StandardMap傳輸對象所請求的Standard實體(手動或使用Mapper.Map<Source, Destination>(source, destination);

  3. 保存已更改

ADDED

如果你想使用AutoMapper你已經正確配置映射器請求嵌套實體從上下文而不是創建他們,像:

Mapper.CreateMap<StudendMap, Student>. 
    ForMember(
     x => x.Standard, 
     m => m.ResolveUsing(
      s => Context.Set<Standard>.Find(s.StandardID))) 
+1

很好,你的情況發生在一個問題用Mapper.Map (source,destination)方法更新實體時的時間。由於源對象(DTO)中包含的所有對象都未由EF進行跟蹤,因此它會將它們添加到上下文中(事實上,某些對象已經存在於上下文中)。所以它會給你例外,因爲鍵保存到數據庫時重複鍵 –

+0

謝謝,看起來像我的問題的解決方案。但是當我的實體包含很多對其他對象的引用時,它就變成了常規 –