I have two classes, a ViewModel and a Dto, that are basically identical except that the Dto has a field 'readonly long? Phone;' while the ViewModel has the a property 'string Phone { get; set; }'.
The only way I've found to get AutoMapper to work is by changing the ViewModel Property to a backing property:
public long? Phone { get; set; }
public string PhoneNumberString
{
get
{
var srv = DependencyResolver.Current.GetService<IPhoneNumberService>();
return srv.GetFormattedPhoneNumber(Phone);
}
set
{
var srv = DependencyResolver.Current.GetService<IPhoneNumberService>();
Phone = srv.GetLongPhoneNumber(value);
}
}
And then in AutoMapper, have a gigantic line to call the constructor:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<MyViewModel, MyDto>()
.ConstructUsing(src => new MyDto(
src.Phone
/* ...Some ~30 other parameters here... */))
.ReverseMap();
});
...There must be a better way to do this? I've tried these:
.ForSourceMember(x => x.PhoneNumberString, opt => opt.DoNotValidate())
and
.ForMember(x => x.PhoneNumberString, opt => opt.Ignore())
and
.ForMember(viewModel => viewModel.Phone, options => options.MapFrom<PhoneNumberResolver>());//PhoneNumberResolver implements IValueResolver<ProspectMaintenanceViewModel, ProspectMaintenanceDto, long?>
Which all give 'Core.DTO.MyDto needs to have a constructor with 0 args or only optional args.' when trying to map, and:
.ForMember(dest => dest.Phone, opt => opt.MapFrom(src => 5))
Which gives 'System.ArgumentException: 'Expression must be writeable' when trying to configure AutoMapper.
Is there some way I can make AutoMapper understand that it can entirely ignore PhoneNumberString (or, even better, some way by which I can make it map long? to string so I don't need the backing property) without having to use the dto's constructor?
Is there any special reason that requires your DTO to not have a default constructor?
I have all my fields as readonly so that I can include a constructor that modifies (e.g. 'Description = description?.Trim();') and validates (e.g. 'if (Phone.HasValue && Phone.ToString().Length != 10) throw ...') the parameters. This way I can ensure that the Dto, being a value object, is always in a valid state.
1) Mapping to readonly field
So you have a Dto class:
public class Dto
{
public readonly long? PhoneNumber;
}
And then you are trying to force AutoMapper to do this:
var dto = new Dto();
dto.PhoneNumber = 123; // <== ERROR! A readonly field cannot be assigned to.
AutoMapper cannot write to readonly fields or properties. In matter of fact you neither. Either turn your field into a property with protected or private setter:
public class Dto
{
public long? PhoneNumber { get; private set; }
}
or make it a regular field by removing the readonly keyword:
public class Dto
{
public long? PhoneNumber;
}
2) Custom value resolving
ASP.NET MVC
Use a ValueResolver:
public class StringPhoneNumberResolver : IValueResolver<Dto, ViewModel, string>
{
private readonly IPhoneNumberService _phoneNumberService;
public StringPhoneNumberResolver()
{
_phoneNumberService = DependencyResolver.Current.GetService<IPhoneNumberService>();
}
public string Resolve(Dto source, ViewModel destination, string destMember, ResolutionContext context)
{
return _phoneNumberService.GetFormattedPhoneNumber(source.PhoneNumber);
}
}
You should know that generally it is an anti-pattern to have service injection in a DTO or IValueResolver. AutoMapper should be dumb and all kind of injections and so on should be handled elsewhere. That being said, here is the AutoMapper configuration:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Dto, ViewModel>()
.ForMember(viewModel => viewModel.PhoneNumber, options =>
options.MapFrom<StringPhoneNumberResolver>());
});
If you want to reverse the process of long ==> string to string ==> long simply add another value resolver:
public class LongPhoneNumberResolver : IValueResolver<ViewModel, Dto, long?>
{
private readonly IPhoneNumberService _phoneNumberService;
public LongPhoneNumberResolver()
{
_phoneNumberService = DependencyResolver.Current.GetService<IPhoneNumberService>();
}
public long? Resolve(ViewModel source, Dto destination, long? destMember, ResolutionContext context)
{
return _phoneNumberService.GetLongPhoneNumber(source.PhoneNumber);
}
}
.NET Core
If you would operate in .NET Core environment, which fully supports IServiceCollection integration, you should add this AutoMapper configuration:
serviceCollection.AddAutoMapper(config =>
{
config.CreateMap<Dto, ViewModel>()
.ForMember(viewModel => viewModel.PhoneNumber, options =>
options.MapFrom<StringPhoneNumberResolver>());
}, typeof(Startup));
and then have IPhoneNumberServce automagically injected into value resolver:
public StringPhoneNumberResolver(IPhoneNumberService phoneNumberService)
{
_phoneNumberService = phoneNumberService;
}
For dependency injection I used automapper.extensions.microsoft.dependencyinjection package.
Well, I found the problem. It has absolutely nothing to do with what I thought it did. Mapping map long? to string works out of the box.
The problem I had was with an entirely different property.
I had the following structure:
public class MyDto
{
public readonly AddressDto BillingAddress;
public readonly AddressDto ShippingAddress;
public readonly long? Phone;
...
}
public class AddressDto
{
public readonly string Country;
public readonly string SubnationalEntity;
...
}
public class MyViewModel
{
public string BillingAddressCountry { get; set; }
public string BillingAddressSubnationalEntity { get; set; }
public string ShippingAddressCountry { get; set; }
public string ShippingAddressSubnationalEntity { get; set; }
public string Phone { get; set; }
...
}
It worked once I changed it to the following:
public class MyDto
{
public readonly AddressDto BillingAddress;
public readonly AddressDto ShippingAddress;
public readonly long? Phone;
...
}
public class AddressDto
{
public readonly string Country;
public readonly string SubnationalEntity;
...
}
public class MyViewModel
{
public string AddressViewModel BillingAddress { get; set; }
public string AddressViewModel ShippingAddress { get; set; }
public string Phone { get; set; }
...
}
public class AddressViewModel
{
public string Country { get; set; }
public string SubnationalEntity { get; set; }
...
}
Related
Is there a way to get the name of an explicit interface property backing field?
For example, for:
public interface I
{
string PPPP { get; }
}
public class C: I
{
private string _other_field = default!; // random private field, just to fill.
public string S => _s_backing; // random property, just to fill.
private string _s_backing = default!;
string I.PPPP => _s_backing; // <--- looking for this one!
}
For property PPPP I'm looking to figure up the string "_s_backing".
I mean. Is there a way to create this helper:
Helpers.DoSomeReflectionMagic( typeof(C), "PPPP" )
// I expect, it returns: `_s_backing`.
What I tried: I was digging into typeof(C) properties, but I didn't find the backing field anywhere. Maybe there is no way to get it.
The underlying XY problem:
public interface ITree<T>
{
T? Parent { get; }
IEnumerable<T> Children { get; }
}
public class Category: ITree<Category>
{
public string Name { get; set; }
public Category? MyParentCategory { get; set; }
public IEnumerable<Category> MySubCategories { get; set; }
// Implementing interface
Category? ITree<Category>.Parent => MyParentCategory;;
IEnumerable<Category> ITree<Category>.Children => MySubCategories;
}
// dbcontext with fluent api to configure model blah blah
public static class DbContextTreeExtensions
{
public static IEnumerable<T> GetRoots<T>(this MyDbContext ctx)
{
var backingfieldname = // <-- The Y problem
Helpers
.DoSomeReflectionMagic(typeof(T), "Parent");
return
ctx
.Set<T>()
.Where(q => EF.Property<T?>(q, backingfieldname) == null)
.AsEnumerable();
}
}
I would like to do:
var ctx = MyDbContextFactory.NewContext();
var mainCategories = ctx.GetRoots<Category>();
I use Mediatr for my CQRS and EF Core for DB access and entity configuration in my API.
I gave my Spending entity a configuration as such:
public class SpendingConfiguration : IEntityTypeConfiguration<Spending>
{
public void Configure(EntityTypeBuilder<Spending> builder)
{
builder.Property(p => p.Cost1)
.HasDefaultValue(6.50)
.IsRequired();
builder.Property(p => p.Cost2)
.HasDefaultValue(6.50)
.IsRequired();
builder.Property(p => p.Cost3)
.HasDefaultValue(6.50)
.IsRequired();
builder.Property(p => p.CurrentSpending)
.HasDefaultValue(0.0)
.IsRequired();
builder.Property(p => p.SpendingLimit)
.IsRequired();
builder.Property(p => p.CreatedDateTime)
.HasDefaultValueSql("getutcdate()");
}
}
Using Mediatr I need to create a AddSpendingCommand and a AddSpendingCommandHandler.
The AddSpendingCommand looks like this:
public class AddSpendingCommand : IRequest<Guid>
{
public string Cost1 { get; set; }
public string Cost2 { get; set; }
public string Cost3 { get; set; }
public string CurrentSpending { get; set; }
public string SpendingLimit { get; set; }
}
And the AddSpendingCommandHandler like this:
public class AddSpendingCommandHandler
: IRequestHandler<AddSpendingCommand, Guid>
{
// variables declaration
public AddSpendingCommandHandler(IMapper mapper,
ISpendingRepository repository)
{
// ctor stuff
}
public async Task<Guid> Handle(
AddSpendingCommand request, CancellationToken cancellationToken)
{
// validation stuff
var convertedSpending = new Spending
{
Cost1 = double.Parse(request.Cost1),
Cost2 = double.Parse(request.Cost2),
Cost3 = double.Parse(request.Cost3),
CurrentSpending = double.Parse(request.CurrentSpending),
SpendingLimit = double.Parse(request.SpendingLimit)
};
// mapping stuff
return spending.SpendingId;
}
}
My question here is, as you can see I've added default values to some properties of the Spending entity.
Do I need to add those properties with default values to the AddSpendingCommand method and manually map them to the AddSpendingCommandHandler's convertedSpending object like when an object needs to map all of its properties to a DTO, or since in this case the user can only modify SpendingLimit, can I just add the properties I want to change, CurrentSpending and SpendingLimit, not add the properties with default values in the AddSpendingCommand and neither in the convertedSpending object and will those properties with default values will be added anyway, on their own, using their default value when I run spending = mapper.Map<Spending>(convertedSpending); to map them to their DTO even though I haven't mentioned them ?
EDIT
The Spending class:
public class Spending
{
public Guid SpendingId { get; set; }
public double Cost1 { get; } // no setters, that's why can't add in Command
public double Cost2 { get; }
public double Cost3 { get; }
public double CurrentSpending { get; set; }
public double SpendingLimit { get; set; }
public DateTime CreatedDateTime { get; set; }
}
What I want to achieve:
AddSpendingCommand
public class AddSpendingCommand : IRequest<Guid>
{
public string CurrentSpending { get; set; }
public string SpendingLimit { get; set; }
}
AddSpendingCommandHandler
public class AddSpendingCommandHandler
: IRequestHandler<AddSpendingCommand, Guid>
{
// variables declaration
public AddSpendingCommandHandler(IMapper mapper,
ISpendingRepository repository)
{
// ctor stuff
}
public async Task<Guid> Handle(
AddSpendingCommand request, CancellationToken cancellationToken)
{
// validation stuff
var convertedSpending = new Spending
{
// Here I would not need to add the other properties,
// their default values would be automatically added,
// that's the question basically
CurrentSpending = double.Parse(request.CurrentSpending),
SpendingLimit = double.Parse(request.SpendingLimit)
};
// mapping stuff
return spending.SpendingId;
}
}
I am assuming that the CurrentSpending and SpendingLimit are strings in the AddSpendingCommand so that you can do some proper validation on those, but that the intent is that they should be invalid when they are not numeric.
Then there are two concerns: where to parse the strings to double, and where to map the command object to your domain object. There are no hard and fast rules of where to implement those concerns, but I will show you what I've done in the past.
For the first concern of parsing the strings to double, you can put them in the command, because that better reflects its intention:
public class AddSpendingCommand : IRequest<Guid>
{
// ... other properties
public string CurrentSpending { get; set; }
public string SpendingLimit { get; set; }
// simple implementation; you can put checks here
internal double CurrentSpendingParsed => double.Parse(CurrentSpending);
internal double SpendingLimitParsed => double.Parse(SpendingLimit);
}
Or if you have default values, for example declared in a Constants file:
public class AddSpendingCommand : IRequest<Guid>
{
// ... other properties
public string CurrentSpending { get; set; }
public string SpendingLimit { get; set; }
internal double CurrentSpendingParsed => double.TryParse(CurrentSpending, out double parsed) ? parsed : Constants.CurrentSpendingDefault;
internal double SpendingLimitParsed => double.TryParse(SpendingLimit, out double parsed) ? parsed : Constants.SpendingLimitDefault;
}
For the second concern of mapping the command object to your domain object, you can put that in your mapper. Technically, you can also put the parsing logic in your mapper, but that makes it more complex than it needs to be. It makes the mapper object take on more responsibility (parsing) than it already has (mapping).
Edit based on comment
If you want to set default values of your Spending entity without having to mention them in the command, you can declare them in the Spending class itself:
public class Spending
{
public double CurrentSpending { get; set; } = 1; // set default
public double SpendingLimit { get; set; } = 10; // set default
}
Those properties will retain those values even if you don't map them from the command.
If the Cost properties have no setters, then their value cannot be set either from the command or even from Entity Framework. You need to compute them from within the Spending class:
public class Spending
{
public double Cost1 { get; } => GetCost1(); // either a value or a method; I'm using a method in this case
private double GetCost1()
{
// calculate cost1 here
}
}
We do a lot of mapping where I work. We use and love AutoMapper.
We would like to validate our mapping profiles. We also often want to ignore some legacy fields. It would be great to use MemberList.Source for this. And it mostly works fine, unless we want some fields to have some sort of special treatment. AutoMapper gets upset during validation if we want to use extension methods on fields or even ValueResolvers.It claims said fields are not mapped.
Is this by design, a bug, am I “holding it wrong” or just missing something obvious? Error message and repro follows.
Both tests errors out with this message
AutoMapper.AutoMapperConfigurationException
# Unmapped members were found. Review the types and members below.
Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type
For no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters
Source -> Destination (Source member list)
AutomapperRepro.Source -> AutomapperRepro.Destination (Source member list)
Unmapped properties:
FieldB
Code:
using AutoMapper;
using Xunit;
namespace AutomapperRepro;
public class MappingTests
{
[Fact]
public void MappingProfile_IsValid()
{
var config = new MapperConfiguration(config => config.AddProfile<MappingProfile>());
config.AssertConfigurationIsValid();
}
[Fact]
public void MappingProfileValueResolver_IsValid()
{
var config = new MapperConfiguration(config => config.AddProfile<MappingProfileValueResolver>());
config.AssertConfigurationIsValid();
}
}
public class MappingProfile: Profile
{
public MappingProfile()
{
CreateMap<Source, Destination>(MemberList.Source)
.ForMember(dest => dest.FieldBPadded, opt => opt.MapFrom(s => s.FieldB.PadFieldB()));
}
}
public class MappingProfileValueResolver : Profile
{
public MappingProfileValueResolver()
{
CreateMap<Source, Destination>(MemberList.Source)
.ForMember(dest => dest.FieldBPadded, opt => opt.MapFrom(new PaddingResolver(), src => src.FieldB));
}
}
public static class PaddingExtentions
{
public static string PadFieldB(this string src)
{
return src.PadLeft(10, '0');
}
}
public class PaddingResolver : IMemberValueResolver<object, object, string, string>
{
public string Resolve(object source, object destination, string sourceMember, string destinationMember,
ResolutionContext context)
{
return sourceMember.PadFieldB();
}
}
public record Source
{
public string FieldA { get; set; } = string.Empty;
public string FieldB { get; set; } = string.Empty;
}
public record Destination
{
public string FieldA { get; set; } = string.Empty;
public string FieldBPadded { get; set; } = string.Empty;
public string SomeLegacyFieldThatCanBeIgnored { get; set; } = string.Empty;
public string SomeLegacyFieldThatCanBeIgnored2 { get; set; } = string.Empty;
public string SomeLegacyFieldThatCanBeIgnored3 { get; set; } = string.Empty;
}
Both suggestions from #LucianBargaoanu are valid👍 Combining MapFrom with a transformer worked great for me:
public class MappingProfileValueTransformer : Profile
{
public MappingProfileValueTransformer()
{
CreateMap<Source, Destination>(MemberList.Source)
.ForMember(dest => dest.FieldBPadded, opt =>
{
opt.MapFrom(s => s.FieldB);
opt.AddTransform(dest => dest.PadFieldB());
});
}
}
I have the following code
IList<ConfigurationDto> result = new List<ConfigurationDto>();
foreach (var configuration in await configurations.ToListAsync())
{
var configurationDto = _mapper.Map<ConfigurationDto>(configuration);
configurationDto.FilePath = _fileStorage.GetShortTemporaryLink(configuration.FilePath);
result.Add(configurationDto);
}
return result;
How can I use automapper instead if foreach? I can map collection, but how to call _fileStorage.GetShortTemporaryLink for each item?
I have looked at AfterMap but I don't know how to get FilePath from dest and map it to src one by one. Can I use automapper for that?
public class ConfigurationDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Version { get; set; }
public DateTime CreateDateTime { get; set; }
public long Size { get; set; }
public string FilePath { get; set; }
}
You can use the IValueResolver interface to configure your map to map a property from a function. Something like the sample bellow.
public class CustomResolver : IValueResolver<Configuration, ConfigurationDto, string>
{
private readonly IFileStorage fileStorage;
public CustomResolver(IFileStorage fileStorage)
{
_fileStorage= fileStorage;
}
public int Resolve(Configuration source, ConfigurationDto destination, string member, ResolutionContext context)
{
return _fileStorage.GetShortTemporaryLink(source.FilePath);
}
}
Once we have our IValueResolver implementation, we’ll need to tell AutoMapper to use this custom value resolver when resolving a specific destination member. We have several options in telling AutoMapper a custom value resolver to use, including:
MapFrom<TValueResolver>
MapFrom(typeof(CustomValueResolver))
MapFrom(aValueResolverInstance)
Then you should configure your map to use the custom resolver for mapping the FilePath property on ConfigurationDto.
var configuration = new MapperConfiguration(cfg => cfg.CreateMap<Configuration, ConfigurationDto>()
.ForMember(dest => dest.FilePath, opt => opt.MapFrom<CustomResolver>()));
You can see more about custom value resolvers at this link: http://docs.automapper.org/en/stable/Custom-value-resolvers.html
I have a behavioral class TaskBehavior, that has a one-argument constructor and that drives the visibility of properties in a form and at the same time the properties, that should be mapped to database entity.
Secondly, I have a DTO class TaskDto, and lastly a database entity Task:
public class TaskDto
{
public string Name { get; set; }
public string Description { get; set; }
}
public class Task
{
public string Name { get; set; }
public string Description { get; set; }
}
public class TaskBehavior
{
public TaskBehavior(bool secure)
{
NameVisible = true;
DescriptionVisible = secure;
}
public bool NameVisible { get; private set; }
public bool DescriptionVisible { get; private set; }
}
I need to map properties from TaskDto to Task but only those, that are decided by TaskBehavior. The purpose is to have only one parametrized ruleset used to direct visibility and conditional mapping:
void SaveData(TaskDto taskDto)
{
var behavior = new TaskBehavior(false);
var entity = Mapper.Map<Task>(taskDto); // TODO: only map properties based on "behavior" definition
}
The result should be that only Name property is mapped in this case from taskDto to entity (according behavior instance).
Finally I made it working myself. The idea is to use ad-hoc created MappingEngine instance and dynamically created mappings based on SOME parameter. The whole magic is in the class TaskMappingEngineFactory, which furthermore caches the created MappingEngine-s to a static collection to boost the performance.
public static class TaskMappingEngineFactory
{
private static readonly object _lock = new object();
private static Dictionary<Enums.PortalEnum, MappingEngine> _mappingEngines = new Dictionary<Enums.PortalEnum, MappingEngine>();
public static MappingEngine GetMappingEngine(TaskBehavior taskBehavior)
{
lock (_lock)
{
MappingEngine mappingEngine;
if (!_mappingEngines.TryGetValue(taskBehavior.PortalType, out mappingEngine))
{
ConfigurationStore store = new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers);
store.CreateMap<TaskInfoDetail, TaskInfoDetail>()
.ForMember(dst => dst.CanExecuteAutomatically, o => o.Condition((Func<TaskInfoDetail,bool>)(src => taskBehavior.AdvancedPropertiesVisible)))
.ForMember(dst => dst.CanExecuteManually, o => o.Condition((Func<TaskInfoDetail,bool>)(src => taskBehavior.AdvancedPropertiesVisible)));
MappingEngine engine = new MappingEngine(store);
_mappingEngines.Add(taskBehavior.PortalType, mappingEngine = engine);
}
return mappingEngine;
}
}
}
In an "updating" method, I simply add a line creating an engine instance via
the factory. Quite simple, quite straightforward, not so sofisticated, but enough for now;)
var behavior = new TaskBehavior(NopContext.Current.PortalType());
var engine = TaskMappingEngineFactory.GetMappingEngine(behavior);
engine.Map(task, updatedTask);