I upgraded from an old version of AutoMapper and converted my custom resolvers, but I'm having a hard time.
public class ProductMappingProfile : Profile
{
public ProductMappingProfile()
{
CreateMap<Product, ProductViewModel>()
.ForMember(
dest => dest.Model,
opt => opt.ResolveUsing<ModelNameResolver>(src => src.ModelId));
// won't compile
}
}
Where Product has a int? ModelId
property and ProductViewModel has a string Name
property.
public class ModelNameResolver : IValueResolver<short?, string, string>
{
private readonly InventoryService _inventoryService;
public ModelNameResolver(InventoryService inventoryService)
{
_inventoryService = inventoryService;
}
public string Resolve(short? source, string destination, string destMember, ResolutionContext context)
{
if (!source.HasValue)
return "n/a";
return _inventoryService.GetModel(source.Value)
.Name;
}
}
The type 'MyNamespace.Web.Resolvers.ModelCodeResolver' cannot be used as type parameter 'TValueResolver' in the generic type or method `'AutoMapper.IMemberConfigurationExpression<TSource,TDestination,TMember>.ResolveUsing<TValueResolver>()'`.
There is no implicit reference conversion from `'MyNamespace.Web.Resolvers.ModelCodeResolver'` to `'AutoMapper.IValueResolver<Data.Models.Product,Web.ViewModels.ProductViewModel,string>'`.
What am I doing wrong? I suspect I am misunderstanding the new custom resolver interface.
IValueResolver interface should be parametrized with source object type, destination object type and type of destination member (that should be type of Resolve method result). In your case parameters should be
IValueResolver<Product, ProductViewModel, string>
But you are creating resolver which is parametrized with
IValueResolver<short?, string, string>
short?
is not your source object type
string
is not your destination object type
You should use something like:
public class ModelNameResolver : IValueResolver<Product, ProductViewModel, string>
{
private readonly InventoryService _inventoryService;
public ModelNameResolver(InventoryService inventoryService)
{
_inventoryService = inventoryService;
}
public string Resolve(Product source, ProductViewModel destination,
string destMember, ResolutionContext context)
{
var modelId = source.ModelId;
if (!modelId.HasValue)
return "n/a";
return _inventoryService.GetModel(modelId.Value).Name;
}
}
And your mapping should look like
var inventoryService = new InventoryService();
var modelNameResolver = new ModelNameResolver(inventoryService);
Mapper.Initialize(c =>
{
c.CreateMap<Product, ProductViewModel>()
.ForMember(dest => dest.Model, opt => opt.ResolveUsing(modelNameResolver));
});
Of course, you can ask your IoC container for instance of model name resolver.
UPDATE: If you want resolver reusable between different source and destination data types, then you have two options:
If your resolver can have parameterless constructor, then you can use IMemberValueResolver
:
public class ModelNameResolver : IMemberValueResolver<object, object, int?, string>
{
// create or assign _inventoryService
// also note objects as source and destination
public string Resolve(object source, object destination,
int? sourceMember, string destMember,
ResolutionContext context)
{
if (!sourceMember.HasValue)
return "n/a";
return _inventoryService.GetModel(sourceMember.Value).Name;
}
}
Usage
.ForMember(dest => dest.Model,
opt => opt.ResolveUsing<ModelNameResolver, int?>(src => src.ModelId)
Second option - do not use resolver. Just use MapFrom
with your custom class which does mapping:
.ForMember(dest => dest.Model,
opt => opt.MapFrom(src => someClass.GetModelName(src.ModelId)));
And someSlass should contain method for getting model name by id
public string GetModelName(int? modelId)
{
if (!modelId.HasValue)
return "n/a";
return _inventoryService.GetModel(modelId.Value).Name;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With