2017-06-27 2 views
1

Ich habe begonnen, this Erweiterung zu verwenden, und möchte nur sagen, es ist sehr gut, danke!Verschieben von Objekten zwischen den Listen AutoMapper und EF6

Jetzt habe ich ein Problem, in dem ein Objekt kann von 1 Sammlung, in einer anderen Sammlung verschoben werden, und wenn ich das tue, erhalte ich eine Ausnahme

InvalidOperationException: Multiplicity Einschränkung verletzt

Ich vermute, das liegt daran, dass das Objekt nicht in der ursprünglichen Sammlung gefunden wird, und diese Erweiterung fügt das Objekt der neuen Sammlung hinzu, obwohl ich es auch verschieben möchte, EF dann beim Speichern die Ausnahme, weil ich habe 2 Objekte mit dem gleichen Schlüssel gegen meinen Kontext.

Aber wie kann ich das zum Funktionieren bringen?

Also, wenn ich die folgende Objektstruktur haben

MyRoot 
    | Collection 
      | MyChild 
        | Collection 
          | MyObject (1) 
      | MyChild 
        | Collection 
          | MyObject (2) 

Wie kann ich MyObject (1) in die gleiche Sammlung wie MyObject (2) bewegen ??

Diese sind alle Basisobjekte, und hier ist ein einfacher Code

public class MyRoot 
{ 
    public int Id { get; set; } 

    public ICollection<MyChild> MyChildren { get; set; } 
} 

public class MyChild 
{ 
    public int Id { get; set; } 

    public int RootId { get; set; } 

    public MyRoot Root { get; set; } 

    public ICollection<MyObject> MyObjects { get; set; } 
} 

public class MyObject 
{ 
    public int Id { get; set; } 

    public string Name { get; set; } 

    public int ChildId { get; set; } 

    public MyChild Child { get; set; } 
} 

Jedes dieser Objekte eine DTO haben, aus Gründen der diesem Beispiel können sagen, nur die Objekte sind genau die gleichen, mit der Erweiterung DTO am Ende (das nicht der Fall in realen Anwendung ist)

in meiner Anwendung habe ich dann ein AutoMapper Profil haben, wie so

internal class MyProfile: Profile 
{ 
    public MyProfile() 
    { 
     this.CreateMap<MyRoot, MyRootDTO>() 
      .ReverseMap(); 

     this.CreateMap<MyChild, MyChildDTO>() 
      .ReverseMap() 
      .EqualityComparison((s, d) => s.Id == d.Id); 

     this.CreateMap<MyObject, MyObjectDTO>() 
      .ReverseMap() 
      .EqualityComparison((s, d) => s.Id == d.Id); 
    } 
} 

auf meiner Web-api cont Roller-Methode, ich habe dies, die sehr einfach ist

public async Task<IActionResult> UpdateAsync([FromBody] MyRootDTO model) 
{ 
    // get the object and all children, using EF6 
    var entity = await _service.GetAsync(model.Id); 

    // map 
    _mapper.Map(model, entity); 

    // pass object now updated with DTO changes to save 
    await _service.UpdateAsync(entity); 

    // return 
    return new OkObjectResult(_mapper.Map<MyRootDTO>(entity)); 
} 

Wenn jemand bitte helfen könnte, das wäre toll!

Antwort

0

Damit dies funktioniert, habe ich EF-Schlüssel nicht geändert, aber eine Methode in meinem AutoMapper-Profil implementiert. Ich habe das Objekt durchlaufen, um zu sehen, ob das Kind in einer anderen Liste war, und wenn ja, wurde das Objekt in diese neue Liste verschoben. Auf diese Weise kann der Autoadapter das Objekt basierend auf der ID noch anpassen.

I hinzugefügt, um den Code unten in die .BeforeMap Methode

Nicht dass mein Basisebene Objekt in diesem Beispiel Root genannt wird, so Parameter sRootModel ist (von meiner Web api) Typen und Parameter d ist Root Typ (von EF). Sowohl RootModel und Root hat eine Sammlung Sections genannt

.BeforeMap((s, d) => 
{ 
    // we are going to check if any child has been moved from 1 parent to another, and 
    // if so, move the child before the mapping takes place, this way AutoMapper.Collections will not 
    // mark the object as orphaned in the first place! 
    foreach (var srcParent in s.Sections) 
    { 
     // only loop through old measures, so ID will not be zero 
     foreach (var srcChild in srcParent.Children.Where(e => e.Id != 0)) 
     { 
      // check if the srcChild is in the same dest parent? 
      var destChild = d.Sections.SelectMany(e => e.Children).Where(e => e.Id == srcChild.Id).FirstOrDefault(); 

      // make sure destination measure exists 
      if (destChild != null) 
      { 
       // does the destination child section id match the source section id? If not, child has been moved 
       if (destChild.ParentId != srcParent.Id) 
       { 
        // now we need to move the child into the new parent, so lets find the destination 
        // parent that the child should be moved into 
        var oldParent = destChild.Parent; 
        var newParent = d.Sections.Where(e => e.Id == srcParent.Id).FirstOrDefault(); 

        // remove child from children collection on oldSection and add to newSection 
        oldParent.Children.Remove(destChild); 

        // if newParent is NULL, it is because this is a NEW section, so we need to add this new section 
        // NOTE: Root is my based level object, so your will be different 
        if (newParent == null) 
        { 
         newParent = new Parent(); 
         d.Sections.Add(newParent); 
         newParent.Root = d; 
         newParent.RootId = d.Id; 
        } 
        else 
        { 
         // change references on the child 
         destChild.Parent = newParent; 
         destChild.ParentId = newParent.Id; 
        } 

        newParent.Children.Add(destChild); 
       } 
      } 
     } 
    } 
}) 
0

Ich glaube nicht, dass Ihr Problem mit AutoMapper hier etwas zu tun hat, es ist ein Entity Framework Problem. Wenn Sie etwas aus einer untergeordneten Sammlung in EF entfernen, wird es nicht automatisch gelöscht, es sei denn, Sie rufen entweder ein .Delete-Objekt darauf auf oder der Schlüssel für das Objekt ist ein zusammengesetzter Schlüssel einschließlich des übergeordneten Elements.

Ich würde vorschlagen, einen zusammengesetzten Schlüssel zu machen, wie die folgenden:

public class MyObject 
{ 
    [Column(Order = 1)] 
    [Key] 
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public int Id { get; set; } 

    public string Name { get; set; } 

    [Column(Order = 0)] 
    [Key] 
    public int ChildId { get; set; } 

    public MyChild Child { get; set; } 
} 

Die [DatabaseGenerated] Option hält die Id Spalt als Identität - EF standardmäßig mit einem zusammengesetzten Schlüssel ist für keine automatische Identität.

Sie können dasselbe auf Ihrer Entität MyChild tun.

+0

wenn ich das tun, EF versucht, die Felder gegen das Kind Tabelle zu erzeugen, wie gut? Soll es das tun? Da dies ein einfaches Beispiel ist, ist es in Ordnung, aber mein aktuelles System verweist überall auf Felder und viele fremde Schlüsselfelder versuchen hinzugefügt zu werden. – Gillardo

+0

Es bedeutet, dass Sie zusätzliche Schlüsselfelder benötigen, um Ihre Beziehungen mit multiplen zu arbeiten. Ebene, aber es liegt an Ihnen, ob Sie diese Funktionalität möchten. Ohne dies müssen Sie speziell auf das EF-Material verweisen, um eine Entity zu verschieben. – Richard

+0

Wenn du sagst "referenziere das EF-Material, um eine Entity zu bewegen", was meinst du? – Gillardo

Verwandte Themen