web-dev-qa-db-fra.com

Passer des objets au mappage d'AutoMapper

Je travaille avec AutoMapper et certaines des valeurs de l'entité mappée sont des variables dans ma méthode actuelle. J'ai essayé de le Google, mais en vain. Puis-je transmettre un ensemble de paires de valeurs clés ou un objet ou quelque chose à mon mappage pour qu'il utilise ces valeurs?

Exemple de modification post-cartographie

//comment variable is a Comment class instance
var imageComment = AutoMapper.Mapper.Map<Data.ImageComment>(comment);
//I want to pass in imageId so I dont have to manually add it after the mapping
imageComment.ImageId = imageId;
30
CWitty

AutoMapper gère ce scénario de paire clé-valeur hors de la boîte.

Mapper.CreateMap<Source, Dest>()
    .ForMember(d => d.Foo, opt => opt.ResolveUsing(res => res.Context.Options.Items["Foo"]));

Puis à l'exécution:

Mapper.Map<Source, Dest>(src, opt => opt.Items["Foo"] = "Bar");

Un peu bavard pour creuser dans les éléments de contexte, mais voilà.

39
Jimmy Bogard

Pour Automapper 6.0.2:

Profil:

public class CoreProfile : Profile
{
    public CoreProfile()
    {
        CreateMap<Source, Dest>()
            .ForMember(d => d.Foo,
                opt => opt.ResolveUsing(
                    (src, dst, arg3, context) => context.Options.Items["Foo"]
                )
            );
    }
}

Mappage:

    var result = Mapper.Map<PlanResult>(aa, opt => {
        opt.Items["Foo"] = 2;
        opt.Items["OtherFoo"] = 1000;
    });
33
Leszek P

Les objets peuvent être transmis au résolveur avec l'option de dictionnaire Items. L'API standard pour ce faire est assez verbeuse (comme on le voit dans la réponse acceptée) mais peut être bien simplifiée en utilisant quelques méthodes d'extension:

/// <summary>
/// Map using a resolve function that is passed the Items dictionary from mapping context
/// </summary>
public static void ResolveWithContext<TSource, TDest, TMember, TResult>(
    this IMemberConfigurationExpression<TSource, TDest, TMember> memberOptions, 
    Func<TSource, IDictionary<string, object>, TDest, TMember, TResult> resolver
) {
    memberOptions.ResolveUsing((src, dst, member, context) => resolver.Invoke(src, context.Items, dst, member));
}

public static TDest MapWithContext<TSource, TDest>(this IMapper mapper, TSource source, IDictionary<string, object> context, Action<IMappingOperationOptions<TSource, TDest>> optAction = null) {
    return mapper.Map<TSource, TDest>(source, opts => {
        foreach(var kv in context) opts.Items.Add(kv);
        optAction?.Invoke(opts);
    });
}

Qui peut être utilisé comme ceci:

// Define mapping configuration
Mapper.CreateMap<Comment, ImageComment>()
    .ForMember(
        d => d.ImageId,
        opt => opt.ResolveWithContext(src, items, dst, member) => items["ImageId"])
    );

// Execute mapping
var context = new Dictionary<string, object> { { "ImageId", ImageId } };
return mapper.MapWithContext<TSource, TDest>(source, context);

Si vous avez un objet que vous devez généralement transmettre aux résolveurs de mappage (par exemple, l'utilisateur actuel), vous pouvez aller plus loin et définir des extensions plus spécialisées:

public static readonly string USER_CONTEXT_KEY = "USER";

/// <summary>
/// Map using a resolve function that is passed a user from the
/// Items dictionary in the mapping context
/// </summary>
public static void ResolveWithUser<TSource, TDest, TMember, TResult>(
    this IMemberConfigurationExpression<TSource, TDest, TMember> memberOptions,
    Func<TSource, User, TResult> resolver
) {
    memberOptions.ResolveWithContext((src, items, dst, member) =>
        resolver.Invoke(src, items[USER_CONTEXT_KEY] as User));
}

/// <summary>
/// Execute a mapping from the source object to a new destination
/// object, with the provided user in the context.
/// </summary>
public static TDest MapForUser<TSource, TDest>(
    this IMapper mapper,
    TSource source,
    User user,
    Action<IMappingOperationOptions<TSource, TDest>> optAction = null
) {
    var context = new Dictionary<string, object> { { USER_CONTEXT_KEY, user } };
    return mapper.MapWithContext(source, context, optAction);
}

Qui peut être utilisé comme:

// Create mapping configuration
Mapper.CreateMap<Source, Dest>()
    .ForMember(d => d.Foo, opt => opt.ResolveWithUser((src, user) src.Foo(user));

// Execute mapping
return mapper.MapWithUser(source, user);
3
mark.monteiro