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这里的@AkmalSalikhovy.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 })