2015-03-18 6 views
5

Ich habe folgende Entity Framework Entitäten:Saving AutoMapper kartiert Sammlungen von Entities Entity Framework mit

public class Region 
{ 
    public int RegionId { get; set; } // Primary Key 
    public string Name { get; set; } 
    public virtual ICollection<Country> Countries { get; set; } // Link Table 
} 
public class Country 
{ 
    public int CountryId { get; set; } // Primary Key 
    public string Name { get; set; } 
    public int RegionId { get; set; } // Foreign Key 
} 

I Karte diese mit AutoMapper den folgenden Viewmodels:

public class RegionViewModel 
{ 
    public int RegionId { get; set; } 
    public string Name { get; set; } 
    public virtual ICollection<int> Countries { get; set; } 
} 
public class CountryViewModel 
{ 
    public int CountryId { get; set; } 
    public string Name { get; set; } 
} 

ich meine Viewmodels übersetzen an Entitäten, die AutoMapper verwenden, damit ich eine neue Region speichern kann. Das ist mein Mapping-Code:

Mapper.CreateMap<RegionViewModel, Region>() 
    .ForMember(x => x.Countries, x => x.MapFrom(y => y.Countries.Select(z => new Country() { CountryId = z }).ToArray())); 

Diese eine Ausnahme verursacht, wenn die Region im Repository hinzugefügt, da es auch eine neue Instanz von Land mit einem Null-Namen zu erstellen versucht. Eine Lösung besteht darin, die Methode Add in meinem Repository zu ändern, um den Zustand der Länderobjekte auf Unverändert zu setzen. Die andere alternative Lösung besteht darin, eine kompliziertere Übersetzungslogik zu verwenden, die ein anderes Repository verwendet, um die echten Länderobjekte zu erhalten. Dieser Ansatz weist eine langsamere Leistung auf, da er einen zusätzlichen Aufruf an die Datenbank vornehmen muss, Sie aber auch ein vollständigeres Region-Objekt erhalten.

Mapper.CreateMap<RegionViewModel, Region>(); 
Mapper.CreateMap<int[], Country[]>().ConvertUsing(x => countryRepository.GetAll().Result.Where(y => x.Contains(y.CountryId)).ToArray()); 

Ich lehne mich an den ersten, aber was ist der richtige Ansatz?

+0

Warum benötigen Sie Auto-Mapper? Könnten Sie die Entität als Ihr Ansichtsmodell verwenden? – stepandohnal

+1

Ich benutze AutoMapper, um Zeit zu sparen beim Schreiben von Code für die Übersetzung von Textbausteinen. Alle, wenn Sie AutoMapper aus der Frage entfernen. Ich denke, die Frage steht immer noch. Was ist der richtige Ansatz? –

Antwort

3

Die erste Methode, zusammen mit der Schleife, die die Zustände zu UnChanged, zu setzen, ist auf jeden Fall die beste. Es ist leicht, weil Sie Country s nicht unnötig aus der Datenbank holen. Stattdessen vom Mapper Teil ...

y.Countries.Select(z => new Country() { CountryId = z }) 

... erstellen Sie Stub Einheiten, das heißt unvollständige Einheiten, die als Platzhalter für die wirklichen Dinge dienen. Das ist eine gemeinsame recommended approach to reduce network traffic.

Die Einstellung der Zustände auf UnChanged ist eine von mehreren Möglichkeiten, den Stub Country s an den Kontext anzuhängen.Sie müssen sie anhängen, bevor Sie base.Add(region) aufrufen (von dem ich anmaße, fügt die Region dem Regions des Kontexts hinzu), weil Add alle Entitäten in einem Objektdiagramm von dem hinzugefügten Element als neu markiert(), wenn sie noch nicht an das angeschlossen sind Kontext.

+0

Eine andere Antwort von Gert hilft auch http://stackoverflow.com/questions/26518508/two-different-objects-with-same-key-for-entity-framework-does-not-work –

+0

'{CountryId = z}' Wie konvertierst du eine Entität hier in int? –

+0

@AkmalSalikhov 'y.Countries' ist eine Liste von ganzen Zahlen. –

1

Nun, ich denke, das Anhängen eines Graphen von Entitäten an den DbContext ist nicht der richtige Ansatz, da Sie viel Code schreiben müssen, um Entity-States zu fixieren, damit EF Ihre Entitäten nicht dupliziert.

Ein sicherer und einfacher Ansatz IMO lädt Ihre Region-Entity aus dem DbContext, fügt dann Country-Entitäten aus der Countries-Auflistung hinzu und entfernt dann SaveChanges.

Sie können eine generische Auflistung Mapping-Verfahren, so etwas wie (nicht getestet) schreiben:

static class EfUtils 
{ 
    public static void SyncCollections<TEntity>(
     ICollection<TEntity> collectionFromDb, 
     IEnumerable<TEntity> collectionFromVm, 
     IEqualityComparer<TEntity> equalityComparer, 
     Action<TEntity, TEntity> syncAction) 
     where TEntity : class, new() 
    { 
     var dbToVmEntitiesMap = new Dictionary<TEntity, TEntity>(); 
     var newEntities = new List<TEntity>(); 

     foreach (var vmEntity in collectionFromVm) 
     { 
      var dbEntity = collectionFromDb.FirstOrDefault(x => equalityComparer.Equals(x, vmEntity)); 
      if (dbEntity == null) 
      { 
       dbEntity = new TEntity(); 
       newEntities.Add(dbEntity); 
      } 

      dbToVmEntitiesMap.Add(dbEntity, vmEntity); 
     } 

     var removedEntities = collectionFromDb.Where(x => !dbToVmEntitiesMap.ContainsKey(x)).ToList(); 

     foreach (var addedOrUpdatedEntityPair in dbToVmEntitiesMap) 
     { 
      syncAction(addedOrUpdatedEntityPair.Key, addedOrUpdatedEntityPair.Value); 
     } 

     foreach (var removedEntity in removedEntities) 
     { 
      collectionFromDb.Remove(removedEntity); 
     } 

     foreach (var newEntity in newEntities) 
     { 
      collectionFromDb.Add(newEntity); 
     } 
    } 
} 

UPDATE

ich die Länder Sammlung editierbar Land view-Modelle enthalten angenommen. Aber eigentlich enthält es die IDs der Länder. In diesem Fall müßten Sie die gleichen hinzufügen/entfernen Muster anwenden:

var regionFromDb = dbContext.Set<Region>().Find(regionVm.RegionId); 
var countriesToRemove = regionFromDb.Countries.Where(x => !regionVm.Countries.Contains(x.CountryId)).ToList(); 
foreach (var country in countriesToRemove) 
{ 
    regionFromDb.Countries.Remove(country); 
} 

var countryIdsToAdd = regionVm.Countries.Where(x => !regionFromDb.Countries.Any(c => c.CountryId == x)).ToList(); 

// Load countries where CountryId in countryIdsToAdd collection 
var countriesToAdd = dbContext.Set<Country>().Where(x => countryIdsToAdd.Contains(x.CountryId)); 
foreach (var country in countriesToAdd) 
{ 
    regionFromDb.Countries.Add(country); 
} 

dbContext.SaveChanges(); 
+0

Dies ist vielleicht der Ansatz, der sicherer und einfacher zu verstehen ist. Sie müssen jedoch zusätzliche Datenbankabfragen durchführen. –

Verwandte Themen