ASP.NET de Base avec les objectifs EF de Base - DTO mappage de la Collection
Je suis en train d'utiliser (POST/PUT) un DTO objet avec une collection d'objets enfants à partir de JavaScript à un ASP.NET de Base (API Web) avec une EF Base de contexte de que mon la source de données.
Le principal DTO classe est quelque chose comme ceci (simplifié de cours):
public class CustomerDto {
public int Id { get;set }
...
public IList<PersonDto> SomePersons { get; set; }
...
}
Ce que je ne sais pas vraiment est de savoir comment la carte au Client, entité de la classe d'une manière qui ne comprennent pas beaucoup de code juste pour trouver des Personnes a été ajouté/modifié/supprimé etc.
J'ai joué un peu avec les AutoMapper mais il ne semble pas vraiment à jouer gentil avec les objectifs EF de Base dans ce scénario (complexe de la structure de l'objet) et les collections.
Après googler pour quelques conseils autour de cela, je n'ai pas trouvé de bonnes ressources autour de ce qu'est une bonne approche serait. Mes questions c'est en gros: dois-je la refonte du JS-client de ne pas utiliser "complexe" Dto ou est-ce quelque chose qui "devrait" être manipulé par une couche de mappage entre mes Otd et modèle d'Entité ou de toute autre solution de bon que je ne suis pas au courant?
J'ai été en mesure de résoudre à la fois les AutoMapper et et par la main de mapping entre les objets, mais aucune des solutions qui se sent bien et rapidement devenir assez complexe avec beaucoup de code réutilisable.
EDIT:
L'article suivant décrit à quoi je fais référence concernant AutoMapper et EF de Base. Son pas de code compliqué, mais je veux juste savoir si c'est la "meilleure" façon de gérer cela.
(Code de l'article est modifié pour s'adapter à l'exemple de code ci-dessus)
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 ci-dessus écrit comme une extension générique de la méthode.
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>);
Edit:
Une chose que je veux vraiment, c'est delcare la AutoMapper de configuration en un seul lieu (le Profil) de ne pas avoir à utiliser le MapCollection() extension à chaque fois que j'utilise le mappeur (ou toute autre solution qui nécessite de compliquer le code de mappage).
J'ai donc créé une méthode d'extension comme ce
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);
}
}
Puis, quand je créer les mappages je nous comme ceci:
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));
});
});
Qui me permettent de l'utiliser comme cela au moment de la cartographie:
_mapper.Map(customerDto, customer);
Et le résolveur prend soin de la cartographie.
AutoMapper
?Si vous avez une structure de données complexes, de la cartographie à quelque chose de complètement différent, le plus souvent complexe. Vous ne devriez avoir à configurer le AutoMapper mappages une fois, cependant.
Cartographie de l'Entité à DTO sont assez directe avec AutoMapper mais de DTO à EF de Base des entités gérées devient très compliqué et fastidieux assez rapide quand avoir des collections etc. @Sampath Ce n'est pas un problème direct avec AutoMapper, il fait ce qu'il suppose de le faire assez bien, mais je ne suis pas sûr que ses le bon outil pour le travail dans ce cas. Ce n'est pas le nombre de fois que j'ai besoin de configurer AutoMapper c'est le montant de la configuration nécessaire, mais comme Mike Brind'a déclaré c'est peut-être un problème plus complexe que ce que je "ressens" il devrait être. De toute façon, ne peut pas vraiment trouver de tout les "meilleures pratiques".
comment pouvons-nous voir votre complexe en utilisant la cartographie ci-dessus exemple simple ? vous devez mettre plus de code ici.
vous êtes peut-être l'incompréhension de ce que je veux dire, je ne parle pas d'un complexe spécifique de la cartographie, j'ai juste se référant à l'objet complexe des graphiques en général, par exemple, les Clients avec des Commandes avec OrderLines etc. Donc, il a pas un super complexe ou compliqué graphique qui doivent être mappés mais juste des conseils généraux si AutoMapper est une bonne solution ou si il y a d'autres meilleure option. Peut-être AutoMapper est le meilleur outil pour le travail?
OriginalL'auteur jmw | 2016-09-12
Vous devez vous connecter pour publier un commentaire.
AutoMapper
est la meilleure solution.Vous pouvez le faire très facilement comme ceci :
Remarque : Parce que
AutoMapper
automatiquement laList<Person>
àList<PersonDto>
.depuis qu'ils ontsame name
, et il y a déjà une cartographie dePerson
àPersonDto
.Si vous avez besoin de savoir comment l'injecter à ASP.net de base,vous devez voir cette article : l'Intégration AutoMapper avec ASP.NET Core DI
De mappage automatique entre les Dto et entités
Cartographie à l'aide des attributs et des méthodes d'extension
Je ne vois aucun problème à utiliser
AutoMapper
avec toutORM
outil. En fait, c'est assez standard chose ces jours-ci.Sinon, vous devez propriétés de la carte une par une manuellement. Ce qui est très lourde tâche.Je suis en utilisantaspnetzero
que mon application du cadre de développement.Sur ce cadreAutoMapper
est le mappage par défaut de l'Api. Ce cadre fournit beaucoup de intégré extensions avecAutoMapper'.They have
opensource " produit également.si vous voulez voir des que je peux partager cette url avec vous.s'il vous plaît laissez-moi savoir si vous en avez besoin.c'est le commercial :aspnetzero.comDonc le code que j'ai ajouté ci-dessus n'est pas nécessaire selon vous ou est-il (ou similaire) ce que je dois faire pour le faire fonctionner?
vous pouvez l'utiliser aussi.on peut écrire à toute extension de la méthode basée sur
Automapper
,Sur ma demande, nous sommes à l'aide deattribute based
Automapper
extensions.l'utilisation est comme ceci :var property = input.Property.MapTo<Property>();
.beaucoup de variantes sont là.mais toutes sont basées surAutoMapper
.En disant AutoMapper est la meilleure solution, c'est comme dire <marque spécifique de beurre> est le meilleur beurre. À l'aide de AutoMapper est sans doute une bonne solution. Mais vous ne pourrez jamais prouver que c'est le meilleur. Qui à part, AutoMapper est grand, mais c'est " juste mon humble avis.
OriginalL'auteur Sampath
J'ai été aux prises avec le même problème pour un certain temps. Après avoir creusé à travers de nombreux articles que j'ai avec ma propre mise en œuvre qui je partage avec vous.
Tout d'abord, j'ai créé une coutume
IMemberValueResolver
.Puis-je configurer AutoMapper et d'ajouter ma cartographie spécifique:
Cette application me permet de personnaliser entièrement mon objet logique de correspondance en raison de
keyMatch
fonction qui est passé dans le constructeur. Vous pouvez également passer une supplémentairesaveOnlyIf
fonction de si vous pour une raison quelconque besoin de vérifier le passé des objets s'ils sont adaptés pour la cartographie (dans mon cas, il y avait quelques objets qui ne devraient pas être mappé et ajouté à la collection, s'ils n'ont pas passer un supplémentaire de validation).Ensuite par exemple dans votre contrôleur si vous souhaitez mettre à jour votre déconnecté graphique, vous devez effectuer les opérations suivantes:
Cela fonctionne pour moi. C'est à vous si cette application vous convient-il mieux que ce que l'auteur de cette question proposée dans son édité le post:)
OriginalL'auteur rosko