I have requirement to map two objects. The requirement is train reservation details which will have towardsjourney and return journey details.
public class ReservationSource
{
public string ReservationNumber{get;set;}
public TravelS towardsTravelS{get;set;}
public TravelS returnTravelS{get;set;}
}
This is the class which is in the ReservationSource class which is to capture towards and return journey details.
public class TravelS
{
public string travelId{get;set;}
public ICollection<JourneyS> Journeys{get;set;}
}
Above is the reservation source object. This source needs a mapping to the destination object. Destination object is given below.
public class ReservationDestination
{
public string ReservationNumber{get;set;}
public TravelD towardsTravelD{get;set;}
public TravelD returnTravelD{get;set;}
}
public class TravelD
{
public string travelId{get;set;}
public ICollection<JourneyD> Journeys{get;set;}
}
public class JourneyD
{
public string JourneyId{get;set;}
}
public class JourneyS
{
public string JourneyId{get;set;}
}
This is my destination object . Here i want to map my source to destination. How do i define mapping config and map .
var config = new mappingConfiguration(cfg=>
{
cfg.CreateMap<ReservationSource,ReservationDestination>()
});
Imapper map = config.CreateMapper();
This part of code maps only the reservationNumber to the destination object. Can someone help me to map all objects. That is towardsTravelS to towardsTravelD and returnTravelS to returnTravelD.
.net core version : 3.1
First of all you forgot to mention this but I assume there also is a class TravelS that looks like this:
public class TravelS
{
public string TravelId { get; set; }
}
There are a few things missing in your configuration. At the moment AutoMapper doesn't know it has to map properties with different names (TowardsTravelS => TowardsTravelD etc) so we have to define those aswell:
cfg.CreateMap<ReservationSource, ReservationDestination>()
.ForMember(dest => dest.ReturnTravelD, opt => opt.MapFrom(src => src.ReturnTravelS))
.ForMember(dest => dest.TowardsTravelD, opt => opt.MapFrom(src => src.TowardsTravelS));
Here we tell AutoMapper that these properties that have different names need to be mapped.
Secondly TravelS and TravelD are different classes so we need to configure them for mapping as well:
cfg.CreateMap<TravelS, TravelD>();
So we now have something like this:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<ReservationSource, ReservationDestination>()
.ForMember(dest => dest.ReturnTravelD, opt => opt.MapFrom(src => src.ReturnTravelS))
.ForMember(dest => dest.TowardsTravelD, opt => opt.MapFrom(src => src.TowardsTravelS));
cfg.CreateMap<TravelS, TravelD>();
});
var mapper = config.CreateMapper();
var source = new ReservationSource
{
ReservationNumber = "9821",
ReturnTravelS = new TravelS
{
TravelId = "1"
},
TowardsTravelS = new TravelS
{
TravelId = "2"
}
};
var destination = mapper.Map<ReservationDestination>(source);
Console.WriteLine(JsonSerializer.Serialize(destination));
Output:
{"ReservationNumber":"9821","TowardsTravelD":{"TravelId":"2"},"ReturnTravelD":{"TravelId":"1"}}
Try it for yourself here: https://dotnetfiddle.net/FfccVR
Add this in your services in startup :
it's reusable and cleaner
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper(Assembly.GetExecutingAssembly());
}
add these interface and class in your project
public interface IMapFrom<T>
{
void Mapping(Profile profile) => profile.CreateMap(typeof(T), GetType());
}
using AutoMapper;
using System;
using System.Linq;
using System.Reflection;
public class MappingProfile : Profile
{
public MappingProfile()
{
ApplyMappingsFromAssembly(Assembly.GetExecutingAssembly());
}
private void ApplyMappingsFromAssembly(Assembly assembly)
{
var types = assembly.GetExportedTypes()
.Where(t => t.GetInterfaces()
.Any(i =>i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>)))
.ToList();
foreach (var type in types)
{
var instance = Activator.CreateInstance(type);
var methodInfo = type.GetMethod("Mapping")
?? type.GetInterface("IMapFrom`1").GetMethod("Mapping");
methodInfo?.Invoke(instance, new object[] { this });
}
}
}
and your source model be like this (map ReservationSource to ReservationSource):
public class ReservationSource : IMapFrom<ReservationSource>
{
public string Name { get; set; }
public string City { get; set; }
public void Mapping(Profile profile)
{
profile.CreateMap<ReservationSource,ReservationDestination>()
.ForMember(dest => dest.ReturnTravelD, opt => opt.MapFrom(src => src.ReturnTravelS))
.ForMember(dest => dest.TowardsTravelD, opt => opt.MapFrom(src => src.TowardsTravelS));
}
}
Related
With the following example (LinqPad):
void Main()
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Source, DestinationNested>()
.ConstructUsing((source, context) => new DestinationNested(source.InnerValue));
cfg.CreateMap<Source, DestinationOuter>()
.ForMember(x => x.OuterValue, y => y.MapFrom(z => z.OuterValue))
.ConstructUsing((source, context) =>
{
return new DestinationOuter(source.OuterValue, context.Mapper.Map<DestinationNested>(source));
});
});
var src = new Source { OuterValue = 999, InnerValue = 111 };
var mapper = config.CreateMapper();
var mapped = mapper.Map<DestinationOuter>(src);
mapped.Dump();
mapper.ConfigurationProvider.AssertConfigurationIsValid();
}
public class Source
{
public int OuterValue { get; set; }
public int InnerValue { get; set; }
}
public class DestinationOuter
{
public int OuterValue { get; private set; }
public DestinationNested destinationNested { get; private set; }
public DestinationOuter(int outerValue, DestinationNested destinationNested)
{
this.OuterValue = outerValue;
this.destinationNested = destinationNested;
}
}
public class DestinationNested
{
public int NestedValue { get; private set; }
public DestinationNested(int nestedValue)
{
this.NestedValue = nestedValue;
}
}
AssertConfigurationIsValid() currently throws an exception regarding the properties as I'm using ContructUsing.
In practice it does map correctly, but I'd like AssertConfigurationIsValid as part of my test suite to look for regressions (without needing to do manual tests of the mapper).
I'd like reassurance that all my properties are mapped from the source to the destination via the contructor. I wish to use a contructor as it's my Domain tier and the contructor enforces the mandatory items.
I don't wish to ignore all private setters via the IgnoreAllPropertiesWithAnInaccessibleSetter() feature, as I might be ignoring something which I haven't actually set.
Ideally I also don't want to need to do manual Ignore() on each of the properties which appear in the constructor as that leaves a gap for code drift.
I've tried various combinations in Automapper but no luck so far.
I suppose it's a static analysis challenge; I wish to know that my contructor covers all properties in the Destination. And I wish to know that the contructor is being passed everything from the source.
I realise that Automapper isn't doing very much automatic at this point, is there a nice way to lean on automapper for this testing or is this instead a static analysis problem?
Here's my take.
static void Main(string[] args)
{
try{
var mapperCfg = new AutoMapper.MapperConfiguration(cfg =>
{
cfg.CreateMap<Source, DestinationOuter>().ForCtorParam("destinationNested", o => o.MapFrom(s => new DestinationNested(s.InnerValue)));
});
mapperCfg.AssertConfigurationIsValid();
var mapper = mapperCfg.CreateMapper();
var src = new Source { OuterValue = 999, InnerValue = 111 };
mapper.Map<DestinationOuter>(src).Dump();
}catch(Exception ex){
ex.ToString().Dump();
}
}
public class Source
{
public int OuterValue { get; set; }
public int InnerValue { get; set; }
}
public class DestinationOuter
{
public int OuterValue { get; }
public DestinationNested DestinationNested { get; }
public DestinationOuter(int outerValue, DestinationNested destinationNested)
{
this.OuterValue = outerValue;
this.DestinationNested = destinationNested;
}
}
public class DestinationNested
{
public int NestedValue { get; private set; }
public DestinationNested(int nestedValue)
{
this.NestedValue = nestedValue;
}
}
After reading lots of docs, stepping through the integration tests with the debugger and a few days of good'ol experimentation, this is the best I have:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Source, DestinationNested>()
.ForCtorParam("nestedValue", x => x.MapFrom(y => y.InnerValue))
.ForMember(x => x.NestedValue, x => x.MapFrom(y => y.InnerValue));
cfg.CreateMap<Source, DestinationOuter>()
.ForPath(x => x.destinationNested.NestedValue, x => x.MapFrom(y => y.InnerValue))
.ForCtorParam("destinationNested", x => x.MapFrom(y => y));
});
I'm pretty happy with it; it's getting rid of the ContructUsing() smell which in my broader codebase was constructing the nested objects. And it's warning me if my destination object is not populated. Ideally, the constructor param string would be type safe, but I understand why it cannot (perhaps this is something for a fun Roslyn code analyser project for another day :-) )
The secret sauce (hot of the press) was the x => x.MapFrom(y => y) coupled with the .ForPath(x => x.destinationNested.NestedValue, x => x.MapFrom(y => y.InnerValue)) it seemed to give AutoMapper enough hints that the destinationNested was related to InnerValue and that the contructor param was renamed to "destinationNested". The magic came in that rather than using the context to contruct the nested object the innocent looking x.MapFrom(y => y) seemed to let it use the property mapping instead*.
*That's my layman's explanation, I haven't yet followed enough of the AutoMapper source code to understand the relationship between property mapping and constructor mapping truely. Reading through some GitHub tickets, I thought they were separate concepts.
I also haven't seen x.MapFrom(y => y) mentioned in the docs, so I'd be interested in learning more about it.
I am using the following mapping to map my data object to viewmodel object.
ObjectMapper.cs
public static class ObjectMapper
{
public static void Configure()
{
Mapper.CreateMap<User, UserViewModel>()
.ForMember(dest => dest.Title,
opt => opt.ResolveUsing<TitleValueResolver>())
.ForMember(dest => dest.Name,
opt => opt.ResolveUsing<NameValueResolver >())
.ForMember(dest => dest.ShortName,
opt => opt.ResolveUsing<ShortNameValueResolver >());
}
}
Parser
public class Parser{
public string GetTitle(string title){
/* add some logic */
return title;
}
public string GetName(string title){
/* add some logic */
return name;
}
public string GetShortName(string title){
/* add some logic */
return shortname;
}
}
AutoMapperCustomResolvers.cs
public class TitleValueResolver : ValueResolver<User, string>
{
private readonly BaseValueResolver _baseResolver;
public TitleValueResolver()
{
_baseResolver = new BaseValueResolver();
}
protected override string ResolveCore(Usersource)
{
return _baseResolver.Parser.GetTitle(source.TITLE);
}
}
public class NameValueResolver : ValueResolver<User, string>
{
private readonly BaseValueResolver _baseResolver;
public NameValueResolver()
{
_baseResolver = new BaseValueResolver();
}
protected override string ResolveCore(Usersource)
{
return _baseResolver.Parser.GetName(source.TITLE);
}
}
public class ShortNameValueResolver : ValueResolver<User, string>
{
private readonly BaseValueResolver _baseResolver;
public ShortNameValueResolver()
{
_baseResolver = new BaseValueResolver();
}
protected override string ResolveCore(Usersource)
{
return _baseResolver.Parser.GetShortName(source.TITLE);
}
}
I am using the above code to add logic to the destination property using the separate custom value resolvers. Not sure is this the right approach.
i) Is there a better way to achieve this?
ii) And how to use unity to resolve in case i want to inject some dependency to custom resolver constructor?
Thanks
As I understand your question, you want to utilize a ValueResolver, that resolves multiple source properties into an intermediate data object, which is used to resolve multiple target properties. As an example, I assume the following source, target, intermediate and resolver types:
// source
class User
{
public string UserTitle { get; set; }
}
// target
class UserViewModel
{
public string VM_Title { get; set; }
public string VM_OtherValue { get; set; }
}
// intermediate from ValueResolver
class UserTitleParserResult
{
public string TransferTitle { get; set; }
}
class TypeValueResolver : ValueResolver<User, UserTitleParserResult>
{
protected override UserTitleParserResult ResolveCore(User source)
{
return new UserTitleParserResult { TransferTitle = source.UserTitle };
}
}
You need a target property in order to utilize opt.ResolveUsing<TypeValueResolver>(). This means, you can establish a mapping, where an appropriate target property is available.
So, for the moment, lets wrap the result into an appropriate container type:
class Container<TType>
{
public TType Value { get; set; }
}
And create a mapping
Mapper.CreateMap<User, Container<UserViewModel>>()
.ForMember(d => d.Value, c => c.ResolveUsing<TypeValueResolver>());
And another mapping
Mapper.CreateMap<UserTitleParserResult, UserViewModel>()
.ForMember(d => d.VM_Title, c => c.MapFrom(s => s.TransferTitle))
.ForMember(d => d.VM_OtherValue, c => c.Ignore());
And another mapping
Mapper.CreateMap<User, UserViewModel>()
.BeforeMap((s, d) =>
{
Mapper.Map<User, Container<UserViewModel>>(s, new Container<UserViewModel> { Value = d });
})
.ForAllMembers(c => c.Ignore());
// establish more rules for properties...
The last mapping is a bit special, since it relies on a nested mapping in order to update the destination with values from source via separately configured mapping rules. You can have multiple different transfer mappings for different properties by adding appropriate intermediate mappings and calls in BeforeMap of the actual mapped type. The properties that are handled in other mappings need to be ignored, since AutoMapper doesn't know about the mapping in BeforeMap
Small usage example:
var user = new User() { UserTitle = "User 1" };
// create by mapping
UserViewModel vm1 = Mapper.Map<UserViewModel>(user);
UserViewModel vm2 = new UserViewModel() { VM_Title = "Title 2", VM_OtherValue = "Value 2" };
// map source properties into existing target
Mapper.Map(user, vm2);
Dunno if this helps you. There might be better ways if you rephrase your question to describe your initial problem instead of what you suspect to be a solution.
I have a viewmodel set up that contains several physical path strings that point to files on a server.
I have virtual directories pointing to the root folders on the server.
What I need to do is change this physical paths into paths that reference the virtual folders, so I can create hyper links in my page views.
What I am hoping to do is have a common resolver that I can pass a key to, then have it return the path that I need.
Here is how I did it. I just want to see if there is a more simple "cleaner" way of doing it.
I have only been working with mvc and c# for a couple of months, so I am still learning.
Thank in advance.
Here is the mapper info
Mapper.CreateMap<FAG_DETAIL, OrderFagDetail>()
.ForMember(dest => dest.TXT01, opt => opt.ResolveUsing<DrawingPathCorrect>())
.ForMember(dest => dest.TXT02, opt => opt.ResolveUsing<Prog1PathCorrect>())
.ForMember(dest => dest.TXT03, opt => opt.ResolveUsing<Prog2PathCorrect>())
.ForMember(dest => dest.TXT04, opt => opt.ResolveUsing<Prog3PathCorrect>())
.ForMember(dest => dest.TXT05, opt => opt.ResolveUsing<Prog4PathCorrect>())
.ForMember(dest => dest.TXT07, opt => opt.ResolveUsing<Procs1PathCorrect>())
.ForMember(dest => dest.TXT08, opt => opt.ResolveUsing<Procs2PathCorrect>())
.ForMember(dest => dest.TXT09, opt => opt.ResolveUsing<Procs3PathCorrect>())
.ForMember(dest => dest.TXT10, opt => opt.ResolveUsing<Procs4PathCorrect>())
.ForMember(dest => dest.TXT11, opt => opt.ResolveUsing<FASPathCorrect>())
.ForMember(dest => dest.TXT06, opt => opt.ResolveUsing<SecondDrawingPathCorrect>());
Here is ModelView along with the resolver that I am currently using. The model FAG_DETAIL that is the soruce for OrderfagDetail is very large so I will not included it here. It is safe to assume that in the source there are properties that match the ModelView properties exactly.
public class OrderFagDetail
{
public decimal NO { get; set; }
public decimal FKNO { get; set; }
public decimal TYP { get; set; }
public string TXT01 { get; set; } //drawing link
public string TXT02 { get; set; } //First Op program or L20 Program
public string TXT03 { get; set; } //Second op program or K16 Program
public string TXT04 { get; set; } //Third op Program
public string TXT05 { get; set; } //Fourth op Program
public string TXT06 { get; set; } //Second drawing
public string TXT07 { get; set; } //First Op process sheet
public string TXT08 { get; set; } //Second Op process sheet
public string TXT09 { get; set; } //Third Op process sheet
public string TXT10 { get; set; } //Fourth Op process sheet
public string TXT11 { get; set; } //First Article link
}
public interface IValueResolver
{
ResolutionResult Resolve(ResolutionResult source);
}
public class SecondDrawingPathCorrect : ValueResolver<FAG_DETAIL, string>
{
protected override string ResolveCore(FAG_DETAIL detail)
{
PathResolver pr = new PathResolver();
return (pr.ResolvePath(detail.TXT06,"PDFs\\"));
}
}
public class PathResolver
{
public string ResolvePath(string strSrc, string strKey)
{
string corrected = "";
if (strSrc.Length > 0)
{
string inputdetail = strSrc;
corrected = inputdetail.Substring(inputdetail.IndexOf(strKey)+strKey.Length, inputdetail.Length - inputdetail.IndexOf(strKey)-strKey.Length);
}
return (corrected);
}
}
maybe for improvement you can make the PathResolver class static or singleton
the difference is
Static Class:
You cannot create the instance of static class.
Loaded automatically by the .NET Framework common language runtime
(CLR) when the program or namespace containing the class is loaded.
Static Class cannot have constructor.
We cannot pass the static class to method.
We cannot inherit Static class to another Static class in C#.
Singleton:
You can create one instance of the object and reuse it.
Singleton instance is created for the first time when the user
requested.
Singleton class can have constructor.
You can create the object of singleton class and pass it to method.
Singleton class does not say any restriction of Inheritance.
We can dispose the objects of a singleton class but not of static
class.
Assuming that you have multiple CreateMaps like this:
Mapper.CreateMap<PdfFileObject, MyViewModel>()
.ForMember( d => d.Uri, opt => opt.ResolveUsing(something with "pdfs")
Mapper.CreateMap<ImageFileObject, MyViewModel>()
.ForMember( d => d.Uri, opt => opt.ResolveUsing(something with "images")
..etc
Create your resolver like this:
public class SecondDrawingPathCorrect<TFILE> : ValueResolver<FAG_DETAIL, string>
where TFILE : FAG_DETAIL
{
private readonly string _replaceKey;
public SecondDrawingPathCorrect(string replaceKey)
{
_replaceKey = replaceKey;
}
protected override string ResolveCore(FAG_DETAIL detail)
{
string corrected = "";
if (detail.TXT06.Length > 0)
{
string inputdetail = detail.TXT06;
corrected = inputdetail.Substring(inputdetail.IndexOf(_replaceKey) + 5, inputdetail.Length - inputdetail.IndexOf("_replaceKey) - 5);
}
return corrected;
}
}
Register your maps like this:
Mapper.CreateMap<PdfFileObject, MyViewModel>()
.ForMember( d => d.Uri, opt => opt.ResolveUsing(new SecondDrawingPathCorrect<PdfFileObject>("PDFs");
For the above to work, your PfdFileObject, ImageFileObject, etc, should implement/inherit FAG_DETAIL.
There is another option too, if you can control FAG_DETAIL, you can expose on it a property string FilePathPrefix (which for PdfFileObject will return "PDFs"). Then you can use it in the resolver itself (i.e. in the "replace" part use detail.FilePrefix instead of _replaceKey).
With the latest one, you can drop the generic part of the resolver , and do registration like this:
Mapper.CreateMap<FAG_DETAIL, MyViewModel>()
.ForMember( d => d.Uri, opt => opt.ResolveUsing<SecondDrawingPathCorrect>()
.Include<PdfFileObject, MyViewModel>()
.Include<ImageFileObject, MyViewModel>()
...more includes;
I'm new with AutoMapper but I've read a few tutorials and decided to try it.
In those tutorials there was a good idea which I decided to adopt. The authir suggested that the mapping code for the view model should stay in the view model and not in the AutoMapper configuration. This will make it smaller and easily readable:
Here are the base files to do this using reflections, AutoMapperConfiguration:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using AutoMapper;
public class AutoMapperConfig
{
private Assembly assembly;
public AutoMapperConfig(Assembly assembly)
{
this.assembly = assembly;
}
public void Execute()
{
var types = this.assembly.GetExportedTypes();
LoadStandardMappings(types);
LoadCustomMappings(types);
}
private static void LoadStandardMappings(IEnumerable<Type> types)
{
var maps = from t in types
from i in t.GetInterfaces()
where i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapFrom<>) && !t.IsAbstract && !t.IsInterface
select new { Source = i.GetGenericArguments()[0], Destination = t };
foreach (var map in maps)
{
Mapper.CreateMap(map.Source, map.Destination);
}
}
private static void LoadCustomMappings(IEnumerable<Type> types)
{
var maps = from t in types
from i in t.GetInterfaces()
where typeof(IHaveCustomMappings).IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface
select (IHaveCustomMappings)Activator.CreateInstance(t);
foreach (var map in maps)
{
map.CreateMappings(Mapper.Configuration);
}
}
}
public interface IMapFrom<T>
{
}
public interface IHaveCustomMappings
{
void CreateMappings(IConfiguration configuration);
}
The IMapFrom and IHaveCustom interfaces are here only to mark the mapping classes. Now we come to the interesting part. When I do for example a class like the following
public class BasicAddressViewModel : IHaveCustomMappings
{
public string Id { get; set; }
[Display(Name = "Name")]
public string Label { get; set; }
[Display(Name = "Number")]
public string Location { get; set; }
public void CreateMappings(IConfiguration configuration)
{
var map = configuration.CreateMap<Address, BasicAddressViewModel>();
map.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id));
map.ForMember(dest => dest.Label, opt => opt.MapFrom(src => src.Label));
map.ForMember(dest => dest.Location, opt => opt.MapFrom(src => src.Location));
}
}
I'm setting the mappings in the CreateMappings method but if I decide to use this class as a parent those mappings will not be available to my child class and therefor I will have to reuse the same code for all my children classes:
public class IndexAddressViewModel : BasicAddressViewModel
{
public void CreateMappings(IConfiguration configuration)
{
var map = configuration.CreateMap<Address, IndexAddressViewModel >();
map.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id));
map.ForMember(dest => dest.Label, opt => opt.MapFrom(src => src.Label));
map.ForMember(dest => dest.Location, opt => opt.MapFrom(src => src.Location));
}
}
In reality what I want to achieve is this
public class IndexAddressViewModel : BasicAddressViewModel
{
}
Thanks in advance for any suggestions.
You can create a protected method in your base class that creates the custom mappings
protected virtual IMappingExpression<BasicAddressViewModel, TDestination> CreateBaseMappings<TDestination>(IMappingExpression<BasicAddressViewModel, TDestination> mappingExpression)
where TDestination : BasicAddressViewModel
and make your CreateMappings(IConfiguration configuration) method virtual so you can override it in your derived class and call the base CreateBaseMappings method that returns IMappingExpression<BasicAddressViewModel, TDestination> which means you can add more mappings for other members
EDIT
I found a better solution. :)
Since AutoMapper 2.0 you can use the IncludeBase<BaseModel, BaseViewModel>() to call the mapping for the base type. So the new solution is to make your CreateMappings(IConfiguration configuration) method virtual in the base ViewModel class and override it in your derived class where you call:
configuration.CreateMap().IncludeBase();
Maybe this is by design, but we initially did not expect automapper to catch and ignore all NullReferenceExceptions in our mappings. We mostly use the MapFrom and create sometimes complex expressions. We want these mappings to fail if there's any exception, even a NullReferenceException, but we cant get AutoMapper to do that. Is there any way to make automapper not ignore all these exceptions without having to write a custom value resolver for every case? This would mean alot of extra code for us, so much in fact that it probably would be less code without using automapper in the first place.
These are the test that we would expect to all pass:
[TestFixture]
public class Tests
{
[SetUp]
public void Setup() { Mapper.Reset(); }
[Test]
public void ShouldThrowMappingExceptionUsingMapFromExpression()
{
Mapper.CreateMap<Source, Destination>()
.ForMember(d => d.DestinationMember, o => o.MapFrom(s => s.SourceMember.SourceProperty))
;
Assert.Throws<AutoMapperMappingException>(() => Mapper.Map<Source, Destination>(new Source()));
}
[Test]
public void ShouldThrowMappingExceptionUsingResolveUsingExpression()
{
Mapper.CreateMap<Source, Destination>()
.ForMember(d => d.DestinationMember, o => o.ResolveUsing(s => s.SourceMember.SourceProperty))
;
Assert.Throws<AutoMapperMappingException>(() => Mapper.Map<Source, Destination>(new Source()));
}
[Test]
public void ShouldThrowMappingExceptionUsingResolverInstance()
{
Mapper.CreateMap<Source, Destination>()
.ForMember(d => d.DestinationMember, o => o.ResolveUsing(new TestValueResolver()).FromMember(s => s.SourceMember))
;
Assert.Throws<AutoMapperMappingException>(() => Mapper.Map<Source, Destination>(new Source()));
}
[Test]
public void ShouldThrowMappingExceptionUsingResolverType()
{
Mapper.CreateMap<Source, Destination>()
.ForMember(d => d.DestinationMember, o => o.ResolveUsing<TestValueResolver>().FromMember(s => s.SourceMember))
;
Assert.Throws<AutoMapperMappingException>(() => Mapper.Map<Source, Destination>(new Source()));
}
}
public class Destination
{
public string DestinationMember { get; set; }
}
public class Source
{
public SourceChild SourceMember { get; set; }
}
public class SourceChild
{
public string SourceProperty { get; set; }
}
public class TestValueResolver : ValueResolver<SourceChild, string>
{
protected override string ResolveCore(SourceChild source)
{
return source.SourceProperty;
}
}
This behaviour has now been changed in AutoMapper! :-)
https://github.com/AutoMapper/AutoMapper/issues/122