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.

quel est le problème avec 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