AutoMapper not working with covariant interface - c#

using AutoMapper;
namespace MapTest
{
class Program
{
static void Main(string[] args)
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<X, Interf<object>>()
.ForMember(dest => dest.B, opt => opt.MapFrom(src => src.K))
.ForMember(dest => dest.A, opt => opt.Ignore());
});
X x = new X { K = 1 };
Interf<object> instance = new Cl<Y> { A = new Y() };
Mapper.Map(x, instance);
}
}
class Cl<T> : Interf<T>
{
public T A { get; set; }
public int B { get; set; }
}
interface Interf<out T>
{
T A { get; }
int B { get; set; }
}
class X
{
public int K { get; set; }
}
class Y
{
}
}
This code will cause an exception at Mapper.Map(x, instance), because AutoMapper seems to be unable to find the X -> Interf<object> map for mapping to the Cl<Y> object, even though I'm giving it a hint that instance is indeed of type Interf<object>. If I change the CreateMap line to:
cfg.CreateMap<X, Interf<Y>>()
it no longer crashes, because it can resolve Cl<Y> to Interf<Y>.
Is there any way to fix this?

Related

AutoMapper not mapped IncludeMembers with inheritance (interface or abstract base class)

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?

How to map Complex type using automapper

Automapper with complex nested mapping. I am trying to map mydestinationArrayField and
dest1Array, here source objectlist to be copied to dest1array.
here are my classes for source and destination.
namespace AutomapperDemo
{
class Program
{
static void Main(string[] args)
{
try
{
SourceObject request = new SourceObject()
{
sourceTypeField = "1",
SourceObj1Field = new SourceObj1
{
SourceObj1Id = "1",
SourceObjListss = new List<SourceInnerObjList>
{
new SourceInnerObjList
{
SourceObjListItem1Id = 1
},
new SourceInnerObjList
{
SourceObjListItem1Id = 2
}
}
}
};
var mapper = CreateMapper();
DestinationObject destination = new DestinationObject();
destination = mapper.Map<DestinationObject>(request);
}
catch (Exception ex)
{
throw ex;
}
}
public static IMapper CreateMapper()
{
var config = new MapperConfiguration(cfg =>
{
cfg.AllowNullDestinationValues = true;
cfg.CreateMap<SourceObject, DestinationObject>()
.ForMember(dest => dest.destinationTypeField, o => o.MapFrom(src => src.sourceTypeField))
.ForMember(dest => dest.destinationObjectArrayField, o => o.MapFrom(src => new destinationObjectArray()
{
mydestinationArrayField = src.SourceObj1Field.SourceObjListss.Select(x => x.SourceObjListItem1Id).FirstOrDefault().ToString(), //this gives error
//dest1Array = src.SourceObj1Field.SourceObjListss // here source objectlist to be copied to dest1array
}));
});
return config.CreateMapper();
}
}
}
namespace Automapper
{
public class SourceObject
{
public string sourceTypeField;
public SourceObj1 SourceObj1Field { get; set; }
}
public class SourceObj1
{
public string SourceObj1Id { get; set; }
public ICollection<SourceInnerObjList> SourceObjListss { get; set; }
}
public class SourceInnerObjList
{
public int SourceObjListItem1Id { get; set; }
public int SourceObjListItem2d { get; set; }
}
public class SourceInnerObj2List
{
public int? mycount { get; set; }
public int? yourcount { get; set; }
}
}
namespace Automapper
{
public class DestinationObject
{
public string destinationTypeField;
public destinationObjectArray[] destinationObjectArrayField;
}
public class destinationObjectArray
{
public string mydestinationArrayField;
public string myField1;
public destinationInnerObject1Array[] dest1Array;
public destinationInnerObject2Array[] dest2Array;
}
public class destinationInnerObject1Array
{
public string destinationInnerObjectItem11;
public string destinationInnerObjectItem21;
}
public class destinationInnerObject2Array
{
public string categoryTypeField;
public string valueField;
public string NumberField;
}
}
While executing the mapping i am getting "Missing type map configuration or unsupported mapping."
No matter how I configure ignores or custom mappings it seems to not like this nesting. Any Automapper experts out there who could tell me how a mapping with a complex object like this could be done.
It seems like your second ForMember doesn't work:
.ForMember(dest => dest.destinationObjectArrayField, o => o.MapFrom(src => new destinationObjectArray() //...
Because in you're defining your map to return destinationObjectArray()
and not destinationObjectArray[]!
So there is a map like:
destinationObjectArrayField -> destinationObjectArray()
and there is no map like:
destinationObjectArrayField -> destinationObjectArray[]
And Automapper is telling you that.
You should do something like this:
cfg.CreateMap<SourceObject, DestinationObject>()
.ForMember(dest => dest.destinationTypeField, o => o.MapFrom(src => src.sourceTypeField))
.ForMember(dest => dest.destinationObjectArrayField,
o => o.MapFrom(
src => src.SourceObj1Field
.SourceObjListss
.Select(x => new destinationObjectArray
{
myField1 = $"first_id: {x.SourceObjListItem2d} second_id: {x.SourceObjListItem1Id}"
})
.ToArray()
));
});
Also cleaning and formatting code is highly suggested, it's seems like you've simply got lost in it! IDE's can help with that.

Why can't I get Automapper Parameterization to work

Given the following Automapper profile :
public class MyProfile: Profile
{
private int? _injectedInt;
public MyProfile()
{
CreateMap<objectA, objectB>()
.ForMember(e => e.myNullableInt, x => x.MapFrom(s =>_injectedInt.HasValue ? 100 : 0));
}
}
And the following code :
var result = queryableOfObjectA.ProjectTo<objectB>(new { _injectedInt = 1 });
var resultingValue = result.FirstOrDefault().myNullableInt;
Why is "resultingValue" returning 0 instead of 100?
I can't see what I have done wrong from the docs:
http://docs.automapper.org/en/stable/Queryable-Extensions.html#parameterization
I think the place where _injectedInt is declared might be the issue. I have written following test and 100 is returned.
[TestClass]
public class UnitTest19
{
[TestMethod]
public void TestMethod1()
{
Mapper.Initialize(expression => expression.AddProfile(new MyProfile()));
var queryableOfObjectA = new List<objectA>
{
new objectA
{
myNullableInt = 10
}
};
var result = queryableOfObjectA.AsQueryable().ProjectTo<objectB>(new { _injectedInt = (int?)1 });
var resultingValue = result.FirstOrDefault()?.myNullableInt;
Assert.AreEqual(100, resultingValue);
}
}
public class MyProfile : Profile
{
public MyProfile()
{
int? _injectedInt = null;
CreateMap<objectA, objectB>()
.ForMember(e => e.myNullableInt, x => x.MapFrom(s => _injectedInt.HasValue ? 100 : 0));
}
}
public class objectA
{
public int myNullableInt { get; set; }
}
public class objectB
{
public int myNullableInt { get; set; }
}

How to configure AutoMapper to be case sensitive?

I expect the following test to fail, but it doesn't. How can I configure AutoMapper to be case sensitive?
public class AutomapperTests
{
[Fact]
public void CaseSensitiveTest()
{
Mapper.Initialize(cfg => cfg.AddMemberConfiguration().AddName<CaseSensitiveName>());
Mapper.Initialize(cfg => cfg.CreateMap<Source, Destination>());
Mapper.AssertConfigurationIsValid();
}
public class Source
{
public int Foo { get; set; }
}
public class Destination
{
public int FoO { get; set; }
}
}
I'm using version 5.1.1 of AutoMapper.
Take a look at the naming convention configurations: https://github.com/AutoMapper/AutoMapper/wiki/Configuration#naming-conventions
At the Profile or Mapper level you can specify the source and destination naming conventions:
Mapper.Initialize(cfg => {
cfg.SourceMemberNamingConvention = new LowerUnderscoreNamingConvention();
cfg.DestinationMemberNamingConvention = new PascalCaseNamingConvention();
});
Or:
public class OrganizationProfile : Profile
{
public OrganizationProfile()
{
SourceMemberNamingConvention = new LowerUnderscoreNamingConvention();
DestinationMemberNamingConvention = new PascalCaseNamingConvention();
//Put your CreateMap... Etc.. here
}
}

map configuration or unsupported mapping

I have two types. One in the business layer:
namespace Business
{
public class Car
{
private int _id;
private string _make;
private string _model;
public int id
{
get { return _id; }
set { _id = value; }
}
public string make
{
get { return _make; }
set { _make = value; }
}
public string model
{
get { return _model; }
set { _model = value; }
}
}
}
and the other in the Data layer (Entity Framework):
namespace Data
{
using System;
using System.Collections.Generic;
public partial class Car
{
public Car()
{
this.facttables = new HashSet<facttable>();
}
public int id { get; set; }
public string make { get; set; }
public string model { get; set; }
public virtual ICollection<facttable> facttables { get; set; }
}
}
Here is the code I get from the service layer:
namespace Data
{
public class VehicleDAO : IVehicleDAO
{
private static readonly ILog log = LogManager.GetLogger(typeof(VehicleDAO));
MapperConfiguration config;
public VehicleDAO ()
{
Mapper.Initialize(cfg => cfg.CreateMap<Business.Car, Data.Car>());
config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Business.Car, Data.Car>()
.ForMember(dto => dto.facttables, opt => opt.Ignore());
//.ForMember(d => d.id, opt => opt.MapFrom(c => c.id))
//.ForMember(d => d.make, opt => opt.MapFrom(c => c.make))
//.ForMember(d => d.model, opt => opt.MapFrom(c => c.model));
});
config.AssertConfigurationIsValid();
}
public Data.Car Select(int id)
{
Data.Car car;
using (VehicleEntities VehicleDatabase = new VehicleEntities())
{
car = VehicleDatabase.Cars.Where(c => c.id == id).ToList().Single();
Business.Car cars = AutoMapper.Mapper.Map<Business.Car>(car);
}
return car;
}
The exception is: {"Missing type map configuration or unsupported mapping.\r\n\r\nMapping types:\r\nCar_70BD8401A87DAAD8F5F0EC35BCAE5C9E6EE2D6CB5A1AFCE296B313D8AD87D2E9 -> Car\r\nSystem.Data.Entity.DynamicProxies.Car_70BD8401A87DAAD8F5F0EC35BCAE5C9E6EE2D6CB5A1AFCE296B313D8AD87D2E9 -> Business.Car"}. What is wrong? I have marked the line that causes the exception (third from last line).
Automapper only maps in the direction you created the mapping in. CreateMap<Business.Car, Data.Car> creates a mapping from Business.Car to Data.Car. It looks like you are trying to map from Data.Car to Business.Car, which means you need to CreateMap<Data.Car, Business.Car>
Mapper.Initialize(cfg => cfg.CreateMap<Data.Car, Business.Car>());
config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Data.Car, Business.Car>();
});
config.AssertConfigurationIsValid();

Categories

Resources