具有EF Core的ASP.NET Core-DTO集合映射 [英] ASP.NET Core with EF Core - DTO Collection mapping
问题描述
我试图将DTO对象与子对象的集合一起使用(POST/PUT),这些子对象从JavaScript到以EF Core上下文作为数据源的ASP.NET Core(Web API).
I am trying to use (POST/PUT) a DTO object with a collection of child objects from JavaScript to an ASP.NET Core (Web API) with an EF Core context as my data source.
DTO的主要类是这样的(当然是简化的):
The main DTO class is something like this (simplified of course):
public class CustomerDto {
public int Id { get;set }
...
public IList<PersonDto> SomePersons { get; set; }
...
}
我真正不知道的是如何以一种不包含大量代码的方式将其映射到Customer实体类,仅用于找出已添加/更新/删除了哪些人员.
What I don't really know is how to map this to the Customer entity class in a way that does not include a lot of code just for finding out which Persons had been added/updated/removed etc.
在AutoMapper方面我玩了一些,但在这种情况下(复杂的对象结构)和集合,它在EF Core上似乎玩起来并不好.
I have played around a bit with AutoMapper but it does not really seem to play nice with EF Core in this scenario (complex object structure) and collections.
在搜索了有关此问题的一些建议之后,我还没有找到关于好的方法的任何好的资源.我的问题基本上是:我应该重新设计JS客户端以不使用复杂的" DTO,还是应该由我的DTO和实体模型之间的映射层处理这一问题,或者是否有其他我不愿意的好的解决方案?知道吗?
After googling for some advice around this I haven't found any good resources around what a good approach would be. My questions is basically: should I redesign the JS-client to not use "complex" DTOs or is this something that "should" be handled by a mapping layer between my DTOs and Entity model or are there any other good solution that I am not aware of?
我已经能够使用AutoMapper和手动在对象之间进行映射来解决它,但是没有一个解决方案感觉合适,并且随着许多样板代码而迅速变得非常复杂.
以下文章描述了我所指的有关AutoMapper和EF Core的内容.它的代码并不复杂,但是我只想知道这是否是管理此问题的最佳"方法.
The following article describes what I am referring to regarding AutoMapper and EF Core. Its not complicated code but I just want to know if it's the "best" way to manage this.
(本文中的代码经过编辑以适合上面的代码示例)
(Code from the article is edited to fit the code example above)
http://cpratt.co/using-automapper-mapping-instances/
var updatedPersons = new List<Person>();
foreach (var personDto in customerDto.SomePersons)
{
var existingPerson = customer.SomePersons.SingleOrDefault(m => m.Id == pet.Id);
// No existing person with this id, so add a new one
if (existingPerson == null)
{
updatedPersons.Add(AutoMapper.Mapper.Map<Person>(personDto));
}
// Existing person found, so map to existing instance
else
{
AutoMapper.Mapper.Map(personDto, existingPerson);
updatedPersons.Add(existingPerson);
}
}
// Set SomePersons to updated list (any removed items drop out naturally)
customer.SomePersons = updatedPersons;
上面的代码是作为通用扩展方法编写的.
Code above written as a generic extension method.
public static void MapCollection<TSourceType, TTargetType>(this IMapper mapper, Func<ICollection<TSourceType>> getSourceCollection, Func<TSourceType, TTargetType> getFromTargetCollection, Action<List<TTargetType>> setTargetCollection)
{
var updatedTargetObjects = new List<TTargetType>();
foreach (var sourceObject in getSourceCollection())
{
TTargetType existingTargetObject = getFromTargetCollection(sourceObject);
updatedTargetObjects.Add(existingTargetObject == null
? mapper.Map<TTargetType>(sourceObject)
: mapper.Map(sourceObject, existingTargetObject));
}
setTargetCollection(updatedTargetObjects);
}
.....
_mapper.MapCollection(
() => customerDto.SomePersons,
dto => customer.SomePersons.SingleOrDefault(e => e.Id == dto.Id),
targetCollection => customer.SomePersons = targetCollection as IList<Person>);
我真正想做的一件事是在每次使用映射器(或任何其他需要使映射代码复杂的解决方案)时都不必在一个位置(配置文件)中对AutoMapper配置进行delcare,而不必使用MapCollection()扩展.
One thing I really want is to delcare the AutoMapper configuration in one place (Profile) not have to use the MapCollection() extension every time I use the mapper (or any other solution that requires complicating the mapping code).
所以我创建了这样的扩展方法
So I created an extension method like this
public static class AutoMapperExtensions
{
public static ICollection<TTargetType> ResolveCollection<TSourceType, TTargetType>(this IMapper mapper,
ICollection<TSourceType> sourceCollection,
ICollection<TTargetType> targetCollection,
Func<ICollection<TTargetType>, TSourceType, TTargetType> getMappingTargetFromTargetCollectionOrNull)
{
var existing = targetCollection.ToList();
targetCollection.Clear();
return ResolveCollection(mapper, sourceCollection, s => getMappingTargetFromTargetCollectionOrNull(existing, s), t => t);
}
private static ICollection<TTargetType> ResolveCollection<TSourceType, TTargetType>(
IMapper mapper,
ICollection<TSourceType> sourceCollection,
Func<TSourceType, TTargetType> getMappingTargetFromTargetCollectionOrNull,
Func<IList<TTargetType>, ICollection<TTargetType>> updateTargetCollection)
{
var updatedTargetObjects = new List<TTargetType>();
foreach (var sourceObject in sourceCollection ?? Enumerable.Empty<TSourceType>())
{
TTargetType existingTargetObject = getMappingTargetFromTargetCollectionOrNull(sourceObject);
updatedTargetObjects.Add(existingTargetObject == null
? mapper.Map<TTargetType>(sourceObject)
: mapper.Map(sourceObject, existingTargetObject));
}
return updateTargetCollection(updatedTargetObjects);
}
}
然后,当我创建映射时,我会像这样:
Then when I create the mappings I us it like this:
CreateMap<CustomerDto, Customer>()
.ForMember(m => m.SomePersons, o =>
{
o.ResolveUsing((source, target, member, ctx) =>
{
return ctx.Mapper.ResolveCollection(
source.SomePersons,
target.SomePersons,
(targetCollection, sourceObject) => targetCollection.SingleOrDefault(t => t.Id == sourceObject.Id));
});
});
允许我在映射时像这样使用它:
Which allow me to use it like this when mapping:
_mapper.Map(customerDto, customer);
解析器负责映射.
推荐答案
AutoMapper
是最好的解决方案.
您可以很容易地做到这一点:
You can do it very easily like this :
Mapper.CreateMap<Customer, CustomerDto>();
Mapper.CreateMap<CustomerDto, Customer>();
Mapper.CreateMap<Person, PersonDto>();
Mapper.CreateMap<PersonDto, Person>();
注意:因为AutoMapper
将自动将List<Person>
映射到List<PersonDto>
.因为它们具有same name
,并且已经存在从Person
到PersonDto
的映射.
Note : Because AutoMapper
will automatically map the List<Person>
to List<PersonDto>
.since they have same name
, and there is already a mapping from Person
to PersonDto
.
如果您需要了解如何将其注入ASP.net核心,则必须查看此文章: 将AutoMapper与ASP.NET Core DI集成
If you need to know how to inject it to ASP.net core,you have to see this article : Integrating AutoMapper with ASP.NET Core DI
DTO与实体之间的自动映射
这篇关于具有EF Core的ASP.NET Core-DTO集合映射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!