Source class hierarchy:
public interface IBase {}
public interface IChild1 : IBase { }
public interface IChild2 : IBase { }
public class A
{
public IBase Foo { get; set; }
}
public class B : IChild1
{
public int AA { get; set; }
}
public class C : IChild2
{
public bool BB { get; set; }
}
And Destination type:
public class Dest
{
public int AA { get; set; }
public bool BB { get; set; }
}
AutoMapper configuration:
var config = new MapperConfiguration(c =>
{
c.CreateMap<A, Dest>()
.IncludeMembers(x => x.Foo); // Error!
c.CreateMap<IBase, Dest>()
.Include<B, Dest>()
.Include<C, Dest>();
c.CreateMap<B, Dest>()
.IncludeBase<IBase, Dest>();
c.CreateMap<C, Dest>()
.IncludeBase<IBase, Dest>();
});
I try map A -> Dest, expect inner object Foo flattening mapped in Dest but it's not happening
var a = new A()
{
Foo = new B
{
AA = 42
}
};
var r1 = mapper.Map<IBase, Dest>((IBase) new B
{
AA = 42
});
Assert.Equal(42, r1.AA); // Success
Assert.False(r1.BB); // Success
var r = mapper.Map<Dest>(a);
Assert.Equal(42, r.AA); // Fail
Assert.False(r.BB);
I think there is an error in the automapper method IncludeMembers, or am I wrong in the config somewhere?
Related
Hey I'm trying to map my generic class to concrete class but using it's interface.
My service returns me data which type is
IPaggedResults<Customer>
and I want to be able to map this to
IPaggedResults<CustomerDto>
It works if I invoke mapping with:
_mapper.Map<PaggedResults<CustomerDto>>
but I want use following syntax:
_mapper.Map<IPaggedResults<CustomerDto>>
public class PaggedResults<T> : IPaggedResults<T>
{
public IEnumerable<T> Results { get; protected set; }
public int TotalResults { get; protected set; }
public int TotalPages { get; protected set; }
public int ResultsPerPage { get; protected set; }
public PaggedResults(IEnumerable<T> results, int totalResults, int resultsPerPage)
{
Results = results;
TotalResults = totalResults;
TotalPages = totalResults / resultsPerPage;
ResultsPerPage = resultsPerPage;
}
}
public class CustomerDto
{
public int Id { get; set; }
public string Name { get; set; }
public string NIP { get; set; }
}
My mapper configuration:
public static IMapper Initialize()
=> new MapperConfiguration(cfg =>
{
cfg.CreateMap<CustomerCompany, CustomerDto>();
cfg.CreateMap(typeof(IPaggedResults<>), typeof(PaggedResults<>));
cfg.CreateMap(typeof(IPaggedResults<>), typeof(IPaggedResults<>)).As(typeof(PaggedResults<>));
}).CreateMapper();
Im'using Automapper by Jimmy Bogard.
I could achieve it through the following code:
Create an extension for IMapperConfigurationExpression
public static class IMapperConfigurationExpressionExtensions
{
public static void MapPaggedResults<TSource, TDestination>(this IMapperConfigurationExpression exp){
exp.CreateMap(typeof(PaggedResults<TSource>), typeof(IPaggedResults<TDestination>))
.ConstructUsing((source, ctx) => { return ctx.Mapper.Map<PaggedResults<TDestination>>(source) as IPaggedResults<TDestination>; });
}
}
Then use this configuration:
public static IMapper Initialize()
=> new MapperConfiguration(cfg =>
{
cfg.CreateMap(typeof(IPaggedResults<>), typeof(PaggedResults<>));
cfg.MapPaggedResults<CustomerCompany, CustomerDto>();
}).CreateMapper();
Then both results can be obtained:
var _mapper = Initialize();
IPaggedResults<CustomerCompany> source = new PaggedResults<CustomerCompany>(
new List<CustomerCompany>() { new CustomerCompany() {Id =42, Name = "SomeName", NIP = "someNIP" } }, 1, 1);
var resut = _mapper.Map<PaggedResults<CustomerDto>>(source);
var resut2 = _mapper.Map<IPaggedResults<CustomerDto>>(source);
During a runtime mapping operation (like when you use ResolveUsing or a custom TypeConverter) is it possible to get the container classes (or types at least) of the source and destination members?
I know that when you map one object to another, that the objects don't have to be members of some "parent" or "container" object, but I'm talking about the situation when AutoMapper is recursively copying a complex object.
Here's an example:
Here I'm copying (or setting it up at least) Cars & Boats of "kind A" to "kind B".
public class VehicleCopyProfile : AutoMapper.Profile
{
public VehicleCopyProfile()
{
this.CreateMap<CarA, CarB>();
this.CreateMap<BoatA, BoatB>();
this.CreateMap<WindshieldA, WindshieldB>(
.ConvertUsing((s, d, resContext) =>
{
// *** How can I tell if s is coming from a Car or a Boat? ***
});
}
}
// Cars & Boats each have a Windshield
public class CarA
{
public WindshieldA Windshield {get;set;}
}
public class BoatA
{
public WindshieldA Windshield {get;set;}
}
public class WindshieldA
{
public string Name {get;set;}
}
public class CarB
{
public WindshieldB Windshield {get;set;}
}
public class BoatB
{
public WindshieldB Windshield {get;set;}
}
public class WindshieldB
{
public string Name {get;set;}
}
Here is a solution using AutoMapper ResolutionContext Items as proposed by #Lucian Bargaoanu in comment. The idea is to use Before and After Map to store information in the Resolution Context. I use a Stack to keep track of the whole chain of relationship.
namespace SO51101306
{
public static class IMappingExpressionExtensions
{
public static IMappingExpression<A, B> RegisterChainOfTypes<A, B>(this IMappingExpression<A, B> mapping)
{
mapping.BeforeMap((a, b, ctx) => {
ctx.PushTypeInChainOfTypes(typeof(A));
});
mapping.AfterMap((a, b, ctx) => {
ctx.PopLastTypeInChainOfTypes();
});
return mapping;
}
}
public static class ResolutionContextExtensions
{
const string chainOfTypesKey = "ChainOfTypes";
private static Stack<Type> GetOrCreateChainOfTypesStack(ResolutionContext ctx)
{
var hasKey = ctx.Items.ContainsKey(chainOfTypesKey);
return hasKey ? (Stack<Type>)ctx.Items[chainOfTypesKey] : new Stack<Type>();
}
public static void PushTypeInChainOfTypes(this ResolutionContext ctx, Type type)
{
var stack = GetOrCreateChainOfTypesStack(ctx);
stack.Push(type);
ctx.Items[chainOfTypesKey] = stack;
}
public static Type PopLastTypeInChainOfTypes(this ResolutionContext ctx)
{
var stack = (Stack<Type>)ctx.Items[chainOfTypesKey];
return stack.Pop();
}
public static bool HasParentType(this ResolutionContext ctx, Type parentType)
{
var stack = GetOrCreateChainOfTypesStack(ctx);
return stack.Contains(parentType);
}
}
public class CarCopyProfile : Profile
{
public CarCopyProfile()
{
CreateMap<CarA, CarB>().RegisterChainOfTypes();
CreateMap<BoatA, BoatB>().RegisterChainOfTypes();
CreateMap<WindshieldA, WindshieldB>()
.ConvertUsing((wa,wb,ctx)=> {
if(ctx.HasParentType(typeof(CarA)))
{
Console.WriteLine("I'm coming from CarA");
//Do specific stuff here
}
else if (ctx.HasParentType(typeof(BoatA)))
{
Console.WriteLine("I'm coming from boatA");
//Do specific stuff here
}
return wb;
});
}
}
public class CarA
{
public WindshieldA Windshield { get; set; }
}
public class BoatA
{
public WindshieldA Windshield { get; set; }
}
public class CarB
{
public WindshieldB Windshield { get; set; }
}
public class BoatB
{
public WindshieldB Windshield { get; set; }
}
public class WindshieldA
{
public string Name { get; set; }
}
public class WindshieldB
{
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
Mapper.Initialize(c => c.AddProfile<CarCopyProfile>());
var carA = new CarA{Windshield = new WindshieldA()};
var boatA = new BoatA{Windshield = new WindshieldA()};
var carB = Mapper.Map<CarB>(carA);
var boatB = Mapper.Map<BoatB>(boatA);
}
}
}
This will output:
I'm coming from CarA
I'm coming from boatA
Another way is to use custom value resolver:
class CustomResolver<T1, T2>:IValueResolver ... { ... }
this.CreateMap<CarA, CarB>()
.ForMember(x => x.Windshield , opt => opt.ResolveUsing(new CustomResolver<CarA, CarB>()));
Then in you CustomResolver implementation:
var windshieldB = Mapper.Map<WindshieldB>(windshieldA, x => {x.Items["type1"] = typeof(T1); x.Items["type2"] = typeof(T2);});
And then:
this.CreateMap<WindshieldA, WindshieldB>(
.ConvertUsing((s, d, resContext) =>
{
// resContext.Options.Items["type1"]
});
See http://docs.automapper.org/en/stable/Custom-value-resolvers.html
Why this mapping in the example is not map correctly. When I change the suffix CAST to something else (CAST1) it maps correctly. How should I set the mapper to map correctly with the suffix CAST?
public class Class1
{
public string OBEC { get; set; }
public string OBEC_CAST { get; set; }
}
public class Class2
{
public string Obec { get; set; }
public string ObecCast { get; set; }
}
public void Map()
{
Class1 c1 = new Class1() { OBEC = "obec", OBEC_CAST = "cast obce" };
Class2 c2 = Mapper.Map<Class2>(c1);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
Mapper.Initialize(cfg =>
{
cfg.RecognizePrefixes(new string [0]);
cfg.RecognizePostfixes(new string[0]);
cfg.ClearPrefixes();
cfg.SourceMemberNamingConvention = new LowerUnderscoreNamingConvention();
cfg.DestinationMemberNamingConvention = new PascalCaseNamingConvention();
cfg.CreateMap<Class1, Class2>();
});
}
I'm struggling to understand why my code is working just when I replace the ImportProperty line with the implementor of the interface instead of the interface itself:
[TestFixture]
public class FlyweightTest
{
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
class FlyweightIntegerKeyAttribute : ExportAttribute, IFlyweightKey<int>
{
public int Key { get; private set; }
public FlyweightIntegerKeyAttribute(int key) : base(typeof(IAbstraction)) { Key = key; }
}
public interface IAbstraction { }
[FlyweightIntegerKey(1)]
class Abstraction1 : IAbstraction { }
[FlyweightIntegerKey(2)]
class Abstraction2 : IAbstraction { }
[Test]
public void Test()
{
IMefFlyweight<IAbstraction, IFlyweightKey<int>> lFlyweight = new Flyweight();
Initialize(lFlyweight);
Assert.AreEqual(2, lFlyweight.Abstractions.Count());
Assert.IsTrue(lFlyweight.Abstractions.Select(lazy => lazy.Value).OfType<Abstraction1>().Any());
Assert.IsTrue(lFlyweight.Abstractions.Select(lazy => lazy.Value).OfType<Abstraction2>().Any());
}
//public interface IMefFlyweight<T, TMetadata>
//{
// IEnumerable<Lazy<T, TMetadata>> Abstractions { get; set; }
//}
public class Flyweight : IMefFlyweight<IAbstraction, IFlyweightKey<int>>
{
public IEnumerable<Lazy<IAbstraction, IFlyweightKey<int>>> Abstractions { get; set; }
}
public void Initialize(IMefFlyweight<IAbstraction, IFlyweightKey<int>> #object)
{
var catalog = new AggregateCatalog();
var registrationBuilder = new RegistrationBuilder();
registrationBuilder.ForTypesDerivedFrom<IAbstraction>().Export<Lazy<IAbstraction, IFlyweightKey<int>>>();
//registrationBuilder.ForTypesDerivedFrom<IMefFlyweight<IAbstraction, IFlyweightKey<int>>>().ImportProperty(flyweight => flyweight.Abstractions);
// This one works:
registrationBuilder.ForType<Flyweight>().ImportProperty(flyweight => flyweight.Abstractions);
foreach (var lAssembly in AppDomain.CurrentDomain.GetAssemblies())
catalog.Catalogs.Add(new AssemblyCatalog(lAssembly, registrationBuilder));
var container = new CompositionContainer(catalog);
container.SatisfyImportsOnce(#object, registrationBuilder);
}
The:
registrationBuilder.ForType().ImportProperty(flyweight => flyweight.Abstractions)
works fine but:
registrationBuilder.ForTypesDerivedFrom>>().ImportProperty(flyweight => flyweight.Abstractions)
doesn't although I believe they should be equivalent in my case.
I realized the "import" works only on classes and not on interfaces.
A solution could be importing all the properties:
registrationBuilder.ForTypesDerivedFrom<IImportingInterface>().ImportProperties(_ => true);
or
registrationBuilder.ForTypesDerivedFrom<IImportingInterface>().ImportProperties<ExportClass>(_ => true);
Is it possible to configure AutoMapper to set all properties to default value in case if the source object is null for specified classes? I know that I should use Mapper.AllowNullDestinationValues = false; to do what I want for all classes in application.
Here the sampled code that I use for tests, but it doesn't work
public class A
{
static A()
{
Mapper.Initialize(
config =>
{
config.ForSourceType<B>().AllowNullDestinationValues = false;
config.CreateMap<B, A>()
.ForMember(member => member.Name, opt => opt.Ignore());
});
//Mapper.AllowNullDestinationValues = false;
Mapper.AssertConfigurationIsValid();
}
public void Init(B b)
{
Mapper.DynamicMap(b, this);
}
public int? Foo { get; set; }
public double? Foo1 { get; set; }
public bool Foo2 { get; set; }
public string Name { get; set; }
}
public class B
{
public string Name { get; set; }
public int? Foo { get; set; }
public double? Foo1 { get; set; }
public bool Foo2 { get; set; }
}
Using of this code:
var b = new B() {Foo = 1, Foo1 = 3.3, Foo2 = true, Name = "123"};
var a = new A {Name = "aName"};
a.Init(b); // All ok: Name=aName, Foo=1, Foo1=3,3, Foo2=True
a.Init(null); // Should be Name=aName, Foo=null, Foo1=null, Foo2=False,
// but a has the same values as on a previous line
It must be related to "a" being already mapped.
var a = new A {Name = "aName"};
a.Init(b);
a.Init(null);
All mappings are cached, so if you'll attempt to re-map same instance the automapper would just keep the original result.
In order to test it, try:
var c = new A {Name = "x"};
c.Init(null);
Here is a link to similar question.
Looks like it will replace with null if you set Mapper.Configuration.AllowNullDestinationValues = false:
public class A
{
static A()
{
Mapper.Initialize(
config =>
{
config.ForSourceType<B>().AllowNullDestinationValues = false;
config.CreateMap<B, A>()
.ForMember(member => member.Name, opt => opt.Ignore());
});
Mapper.Configuration.AllowNullDestinationValues = false;
Mapper.AssertConfigurationIsValid();
}
public void Init(B b)
{
Mapper.DynamicMap(b, this);
}
public int? Foo { get; set; }
public double? Foo1 { get; set; }
public bool Foo2 { get; set; }
public string Name { get; set; }
}