Entity framework 编辑/更新EF Core中的集合:从DTO还是从存储库?
我有以下几类:Entity framework 编辑/更新EF Core中的集合:从DTO还是从存储库?,entity-framework,entity-framework-core,automapper,.net-5,ef-core-5.0,Entity Framework,Entity Framework Core,Automapper,.net 5,Ef Core 5.0,我有以下几类:公司,国家,个人 class Country { public int Id { get; set; } public string Name { get; set; } public Person President { get; set; } ICollection<Company> Companies { get; set; } ICollection<Person> People { get; set; } }
公司
,国家
,个人
class Country
{
public int Id { get; set; }
public string Name { get; set; }
public Person President { get; set; }
ICollection<Company> Companies { get; set; }
ICollection<Person> People { get; set; }
}
class Company
{
public int Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public Country Country { get; set; }
ICollection<Person> People { get; set; }
}
class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
我用AutoMapper,我也用
CreateMap<Company, CompanyDTO>()
.ForMember(p => p.CountryName, o => o.MapFrom(p => p.Country.Name))
.ForMember(p => p.PeopleIds, o => o.MapFrom(p => p.People.Select(s => s.Id).ToArray()))
.ForMember(p => p.PeopleNames, o => o.MapFrom(p => p.People.Select(s => s.Name).ToArray()))
.ReverseMap();
从存储库
对于问题的第一部分,Automapper有一个Map
调用,可以将值复制到现有实体
var company = await _repository.SingleAsync(spec);
mapper.Map(companyDto, company);
ReverseMap
应该注意将细节返回到实体中,尽管它不会执行添加/删除关联实体之类的操作。因此,要添加/删除的ID的实现与我使用的方法一致
当涉及到更新数据和关联时,通常建议尽可能原子化地拆分操作;例如,让操作添加或删除国家/地区关联,而不是让客户端更改整个公司对象图,然后通过类似“UpdateCompany”的方法提交更改。“全部”或“一个”更新操作可能涉及相当多的工作,因此我会将其保留在绝对需要的位置。在Steve的建议之后,我构建了一个扩展方法,可以将存储库中的任何现有集合更新为一组ID:
public static class BusinessExtensions
{
/// <summary>
/// Updates a business objects collection (usually from repository)
/// to correspond to the given list of ID's (usually updated in the View)
/// </summary>
/// <typeparam name="T">Any entity having an Id (IdEntity)</typeparam>
/// <param name="collectionToUpdate">reposotory collection to be updated</param>
/// <param name="updatedIds">list of new id's to syncronise with the repository collection</param>
/// <param name="Spec">function transforming a list of ids into a list of business objects from repository</param>
public static void SyncCollection<T>(this ICollection<T> collectionToUpdate, int[] updatedIds, Func<int[], IEnumerable<T>> Spec)
where T : IdEntity
{
var existingEntityIds = collectionToUpdate.Select(x => x.Id).ToList();
var entityIdsToRemove = existingEntityIds.Except(updatedIds).ToArray();
var entityIdsToAdd = updatedIds.Except(existingEntityIds).ToArray();
var entitiesToRemove = Spec(entityIdsToRemove);
var entitiesToAdd = Spec(entityIdsToAdd);
foreach (var entity in entitiesToRemove)
collectionToUpdate.Remove(entity);
foreach (var entity in entitiesToAdd)
collectionToUpdate.Add(entity);
}
}
什么存储库?DbContext已经是一个工作单元,DbSet已经是一个存储库。问题的其余部分也不清楚-您可以使用AutoMapper从API DTO映射到数据库DTO。您不局限于只映射一种方式。如果要基于API DTO更新或创建新的数据库行,请将DTO映射到EF实体并持久化它们。如果适当地使用POST和PUT方法,您将知道DTO是用于新对象还是用于更新对象。如果没有,您应该有不同的创建/更新操作。我有两个可以从中创建业务对象的源:从(DbContext==Repository==数据库),使用Repository对象,或者从视图(=从DTO,使用mapper对象),我将
国家
对象保存在DB中我为这个问题添加了一个扩展方法作为“答案”,如果您使用标识
类似对象,可能会很有用…我现在还没有使用的地图(源、目的地)!我会测试它!谢谢!是的,虽然Automapper的文档通常非常有用,但有关特定方法的详细信息可能有点隐藏。我试图找到地图的官方文档链接,但找不到链接到答案的链接!:)
var company = await _repository.SingleAsync(spec);
mapper.Map(companyDto, company);
public static class BusinessExtensions
{
/// <summary>
/// Updates a business objects collection (usually from repository)
/// to correspond to the given list of ID's (usually updated in the View)
/// </summary>
/// <typeparam name="T">Any entity having an Id (IdEntity)</typeparam>
/// <param name="collectionToUpdate">reposotory collection to be updated</param>
/// <param name="updatedIds">list of new id's to syncronise with the repository collection</param>
/// <param name="Spec">function transforming a list of ids into a list of business objects from repository</param>
public static void SyncCollection<T>(this ICollection<T> collectionToUpdate, int[] updatedIds, Func<int[], IEnumerable<T>> Spec)
where T : IdEntity
{
var existingEntityIds = collectionToUpdate.Select(x => x.Id).ToList();
var entityIdsToRemove = existingEntityIds.Except(updatedIds).ToArray();
var entityIdsToAdd = updatedIds.Except(existingEntityIds).ToArray();
var entitiesToRemove = Spec(entityIdsToRemove);
var entitiesToAdd = Spec(entityIdsToAdd);
foreach (var entity in entitiesToRemove)
collectionToUpdate.Remove(entity);
foreach (var entity in entitiesToAdd)
collectionToUpdate.Add(entity);
}
}
var spec = new CompanyWithPeopleSpecification(id);
var company = await _repository.SingleOrDefaultAsync(spec);
_mapper.Map(comapnyDto, company;
company.People.SyncCollection(companyDto.PeopleIds,
ids => _repository.List(new PeopleFromIdsSpecification(ids)));