Reputation: 291
I want to map ViewModel to Model with base class but without adding base class to ViewModel.
I would like to keep my classes' structure.
I have tried to use Include, IncludeBase, IncludeDerivered but I can't find good configuration.
Here is my code
public class Program
{
private static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var mapper = new MapperConfiguration(mc => mc.AddProfile(new MappingProfile())).CreateMapper();
var model = new RegisterViewModel { Name = "TEST" };
var x = mapper.Map<Register>(model, Guid.NewGuid());
}
}
public interface ICommand
{
Guid Id { get; }
Guid AggregateId { get; }
}
public abstract class Command : ICommand
{
protected Command() => Id = Guid.NewGuid();
public Guid Id { get; }
public Guid AggregateId { get; private set; }
}
public sealed class Register : Command
{
public string Name { get; private set; }
}
public class RegisterViewModel
{
public string Name { get; set; }
}
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<RegisterViewModel, Command>()
.ForMember(x => x.Id, opt => opt.Ignore())
.AddCommandFields()
.Include<RegisterViewModel, Register>();
CreateMap<RegisterViewModel, Register>().IncludeBase<RegisterViewModel, Command>().AddCommandFields();
}
}
public static class MapperExtensions
{
private static readonly string AGGREGATE_KEY = "AGGREGATE";
public static TDestination Map<TDestination>(this IMapper mapper, object source, Guid aggregateId)
{
return mapper.Map<TDestination>(source, opt =>
{
opt.Items[AGGREGATE_KEY] = aggregateId;
});
}
public static IMappingExpression<TSource, TDestination> AddCommandFields<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
where TDestination : ICommand => expression.ForMember(x => x.AggregateId, opt => opt.MapFromAggregate());
private static void MapFromAggregate<TSource, TDestination>(this IMemberConfigurationExpression<TSource, TDestination, Guid> expression) => expression.MapFrom((_, __, ___, context) => context.Items[AGGREGATE_KEY]);
}
For derived properties Automapper correctly works but base properties are not mapped, although MapFromAggregate is invoked.
EDIT
As @Lucian Bargaoanu suggested I wanted to remove extension methods but then I figured out that problem is with method "AddCommandFields".
This code works correctly
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<RegisterViewModel, Command>()
.ForMember(x => x.Id, opt => opt.Ignore())
.ForMember(x => x.AggregateId, opt => opt.MapFromAggregate())
.Include<RegisterViewModel, Register>();
CreateMap<RegisterViewModel, Register>()
.IncludeBase<RegisterViewModel, Command>()
.ForMember(x => x.AggregateId, opt => opt.MapFromAggregate());
}
}
public static class MapperExtensions
{
private static readonly string AGGREGATE_KEY = "AGGREGATE";
public static TDestination Map<TDestination>(this IMapper mapper, object source, Guid aggregateId)
{
return mapper.Map<TDestination>(source, opt =>
{
opt.Items[AGGREGATE_KEY] = aggregateId;
});
}
public static void MapFromAggregate<TSource, TDestination>(this IMemberConfigurationExpression<TSource, TDestination, Guid> expression) => expression.MapFrom((_, __, ___, context) => context.Items[AGGREGATE_KEY]);
}
I have no idea why this extension method breaks my code.
Upvotes: 0
Views: 94
Reputation: 3516
The problem is that the generic constraint is for ICommand
and there is no setter for ICommand.AggregateId
. You can change the constraint to Command
or add the setter for ICommand.AggregateId
.
Upvotes: 1