Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用实体框架保存自动映射的实体集合_C#_.net_Entity Framework_Entity Framework 6_Automapper - Fatal编程技术网

C# 使用实体框架保存自动映射的实体集合

C# 使用实体框架保存自动映射的实体集合,c#,.net,entity-framework,entity-framework-6,automapper,C#,.net,Entity Framework,Entity Framework 6,Automapper,我拥有以下实体框架实体: 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 C

我拥有以下实体框架实体:

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
}
另一种解决方案是使用更复杂的转换逻辑,它使用另一个存储库来获取真实的国家对象。这种方法的性能较慢,因为它必须对数据库进行额外调用,但您也可以获得更完整的区域对象

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

我倾向于第一种方法,但正确的方法是什么?

嗯,我认为将实体图附加到DbContext不是正确的方法,因为它迫使您编写大量代码来修复实体状态,以防止EF复制您的实体

IMO的一种更安全、更简单的方法是从DbContext加载您的Region实体,然后从Countries集合中添加/删除Country实体,然后调用SaveChanges

您可以编写一个通用的集合映射方法,类似(未测试):

静态类EfUtils
{
公共静态集合(
ICollection collectionFromDb,
IEnumerable collectionFromVm,
IEqualityComparer相等comparer,
动作(同步动作)
其中tenty:class,new()
{
var dbtovmentiesmap=新字典();
var newEntities=newlist();
foreach(collectionFromVm中的var vmEntity)
{
var dbEntity=collectionFromDb.FirstOrDefault(x=>equalityComparer.Equals(x,vmEntity));
if(dbEntity==null)
{
dbEntity=newtenty();
newEntities.Add(dbEntity);
}
添加(dbEntity,vmEntity);
}
var removedEntities=collectionFromDb.Where(x=>!dbtovmentiesmap.ContainsKey(x)).ToList();
foreach(dbToVmEntitiesMap中的var ADEDORUPDATEDENTITYPAIR)
{
syncAction(addedOrUpdatedEntityPair.Key、addedOrUpdatedEntityPair.Value);
}
foreach(removedEntities中的var removedEntity)
{
collectionFromDb.Remove(removedEntity);
}
foreach(新实体中的var newEntity)
{
collectionFromDb.Add(新实体);
}
}
}
更新

我假设Countries集合包含可编辑的国家/地区视图模型。 但实际上它包含了国家的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();
var regionFromDb=dbContext.Set().Find(regionVm.RegionId);
var countriesToRemove=regionFromDb.Countries.Where(x=>!regionVm.Countries.Contains(x.CountryId)).ToList();
foreach(CountryStoreMove中的var国家/地区)
{
regionFromDb.Countries.Remove(国家);
}
var countryIdsToAdd=regionVm.Countries.Where(x=>!regionFromDb.Countries.Any(c=>c.CountryId==x)).ToList();
//加载countryIdsToAdd集合中CountryId所在的国家/地区
var countriesToAdd=dbContext.Set(),其中(x=>countryIdsToAdd.Contains(x.CountryId));
foreach(国家/地区中的var STOADD)
{
regionFromDb.Countries.Add(国家);
}
dbContext.SaveChanges();

第一种方法,连同将状态设置为
未更改的循环,绝对是最好的方法。它是轻量级的,因为您不必从数据库中获取
Country
s。取而代之的是,由制图员部分

y.Countries.Select(z => new Country() { CountryId = z })
…您可以创建存根实体,即作为真实对象占位符的不完整实体。这是一个常见的问题


将状态设置为
未更改
是将存根
国家/地区
附加到上下文的几种方法之一。在调用
base.Add(region)
(我假设它会将区域添加到上下文的
Regions
),因为
Add
将对象图中的所有实体标记为新的(
added
),而这些实体尚未连接到上下文。

为什么需要使用automapper?您可以使用实体作为您的视图模型吗?我使用AutoMapper来节省编写样板翻译代码的时间。如果您从问题中删除AutoMapper,则每个问题都会发生。我认为这个问题仍然存在。正确的方法是什么?这也许是更安全、更容易理解的方法。但是,您必须执行额外的数据库查询。Gert的另一个回答也有助于
{CountryId=z}
如何将实体转换为int这里的@AkmalSalikhov
y.Countries
是一个整数列表。这是我第一次阅读EF存根实体。非常感谢。
Mapper.CreateMap<RegionViewModel, Region>();
Mapper.CreateMap<int[], Country[]>().ConvertUsing(x => countryRepository.GetAll().Result.Where(y => x.Contains(y.CountryId)).ToArray());
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);
        }
    }
}
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();
y.Countries.Select(z => new Country() { CountryId = z })