2016-01-23 2 views
2

У меня есть трехслойное приложение. Слоями являются DAL, BL и UI. Я использую automapper для преобразования моих объектов, взятых из контекста базы данных, в уровни уровня выше. Насколько я вижу, сопоставленные объекты больше не отслеживаются EF.Как обновить объект в базе данных с помощью DTO с помощью Entity Framework

В любом случае проблема, с которой я сталкиваюсь, возникает в момент обновления сущностей, существующих в БД, а точнее - для объектов ETO обновления объектов ETO. DTO с верхнего уровня отображается обратно в объект для завершения операции обновления. Однако сопоставлено с объектом DTO, и все его навигационные свойства и коллекции не существуют в контексте, потому что они не отслеживаются.

EF принимает этот объект и его сущности как новую информацию, не принимая во внимание тот факт, что этот объект и некоторые объекты, включенные в него, уже могут существовать в контексте.

Вот пример.

У меня есть две модели, Student и Standard.

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

У меня также есть StudentMap и StandardMap, которые выглядят так же, как модели, упомянутых выше.

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 как добавленные, это поведение при прикреплении объекта с коллекцией.

Чтобы избежать новых учеников, вы должны сначала приложить учащихся всех стандартов к контексту, чтобы они начали отслеживаться (как немодифицированные), а затем присоединить объект 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. Сохранить изменения

ДОБАВЛЕНО

Если вы хотите использовать 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

Спасибо, похоже, решение моей проблемы. Но она становится обычной, когда мой объект содержит много ссылок на другие объекты –

Смежные вопросы