簡體   English   中英

使用Entity Framework保存AutoMapper映射的實體集合

[英]Saving AutoMapper mapped Collections of Entities using Entity Framework

我有以下實體框架實體:

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
}

我使用AutoMapper將這些映射到以下ViewModel:

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

我想使用AutoMapper將我的ViewModel轉換為實體,這樣我就可以保存一個新的Region。 這是我的映射代碼:

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

這會在存儲庫中添加區域時導致異常,因為它還嘗試使用空名稱創建Country的新實例。 一種解決方案是更改存儲庫中的Add方法,以將Country對象的State設置為Unchanged。

public async Task Add(Region region)
{
    foreach (Country country in region.Countries)
    {
        this.Context.Entry(country).State = EntityState.Unchanged;
    }
    await base.Add(region);
}

另一種替代解決方案是使用更復雜的翻譯邏輯,它使用另一個存儲庫來獲取真實的國家/地區對象。 這種方法性能較慢,因為它必須對數據庫進行額外調用,但您還可以獲得更完整的Region對象。

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

我傾向於第一個,但正確的方法是什么?

第一種方法與將狀態設置為UnChanged的循環一起絕對是最好的方法。 它是輕量級的,因為您不必從數據庫中不必要地獲取Country 相反,由映射器部分......

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

...您創建存根實體 ,即用作真實事物占位符的不完整實體。 這是減少網絡流量的常用方法

將狀態設置為UnChanged是將存根Country附加到上下文的幾種方法之一。 你必須在調用base.Add(region)之前附加它們(我假設將區域添加到上下文的Regions ),因為Add標記的對象圖中的所有實體標記為新的(已Added )尚未依附於上下文。

好吧,我認為將實體圖附加到DbContext不是正確的方法,因為它迫使您編寫大量代碼來修復實體狀態以防止EF復制您的實體。

一種更安全,更簡單的方法IMO是從DbContext加載Region實體,然后從Countries集合中添加/刪除Country實體,然后調用SaveChanges。

您可以編寫一個通用集合映射方法,類似於(未測試):

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

我假設Countries集合包含可編輯的Country視圖模型。 但實際上它包含國家的ID。 在這種情況下,您需要應用相同的添加/刪除模式:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM