Given this these classes, how can I map a dictionary of them?
public class TestClass
{
public string Name { get; set; }
}
public class TestClassDto
{
public string Name { get; set; }
}
Mapper.CreateMap<TestClass, TestClassDto>();
Mapper.CreateMap<Dictionary<string, TestClass>,
Dictionary<string, TestClassDto>>();
var testDict = new Dictionary<string, TestClass>();
var testValue = new TestClass() {Name = "value1"};
testDict.Add("key1", testValue);
var mappedValue = Mapper.Map<TestClass, TestClassDto>(testValue);
var mappedDict = Mapper.Map<Dictionary<string, TestClass>,
Dictionary<string, TestClassDto>>(testDict);
Mapping one of them, mappedValue in this case, works fine.
Mapping a dictionary of them ends up with no entries in the destination object.
What am I doing worng?
The problem you are having is because AutoMapper is struggling to map the contents of the Dictionary. You have to think what it is a store of - in this case KeyValuePairs.
If you try create a mapper for the KeyValuePair combination you will quickly work out that you can't directly as the Key property doesn't have a setter.
AutoMapper gets around this though by allowing you to Map using the constructor.
/* Create the map for the base object - be explicit for good readability */
Mapper.CreateMap<TestClass, TestClassDto>()
.ForMember( x => x.Name, o => o.MapFrom( y => y.Name ) );
/* Create the map using construct using rather than ForMember */
Mapper.CreateMap<KeyValuePair<string, TestClass>, KeyValuePair<string, TestClassDto>>()
.ConstructUsing( x => new KeyValuePair<string, TestClassDto>( x.Key,
x.Value.MapTo<TestClassDto>() ) );
var testDict = new Dictionary<string, TestClass>();
var testValue = new TestClass()
{
Name = "value1"
};
testDict.Add( "key1", testValue );
/* Mapped Dict will have your new KeyValuePair in there */
var mappedDict = Mapper.Map<Dictionary<string, TestClass>,
Dictionary<string, TestClassDto>>( testDict );
AutoMapper has changed a bit so it looks more like:
CreateMap<Thing, ThingDto>()
.ReverseMap();
CreateMap<Thing, KeyValuePair<int, ThingDto>>()
.ConstructUsing((t, ctx) => new KeyValuePair<int, ThingDto>(t.id, ctx.Mapper.Map<ThingDto>(t)));
Related
How do I configure the mapper so that this works?
(i.e. the properties from the dynamic object should map to the properties of the class definition with the same letters - ignoring case)
public class Foo {
public int Bar { get; set; }
public int Baz { get; set; }
}
dynamic fooDyn = new MyDynamicObject();
fooDyn.baR = 5;
fooDyn.bAz = 6;
Mapper.Initialize(cfg => {});
Foo result = Mapper.Map<Foo>(fooDyn);
result.Bar.ShouldEqual(5);
result.Baz.ShouldEqual(6);
If your dynamic object implements IDictionary<string,object> (e.g. ExpandoObject) then the following should work. There must be some easier way to do this as anonymous objects are mapped just fine even if the case is different.
Mapper.Initialize(cfg =>
{
cfg.CreateMap<IDictionary<string, object>, Foo>()
.ConstructUsing(d =>
{
Foo foo = new Foo();
foreach (System.Reflection.PropertyInfo prop in typeof(Foo).GetProperties())
{
foreach (KeyValuePair<string, object> entry in d)
{
if (entry.Key.ToLowerInvariant() == prop.Name.ToLowerInvariant())
{
prop.SetValue(foo, entry.Value);
break;
}
}
}
return foo;
});
});
AutoMapper allows you to configure explicit member mapping on the map configuration in this style:
var config = new MapperConfiguration(cfg =>
{
var dynamicMap = cfg.CreateMap<IDictionary<string, object>, SomethingDTO>();
dynamicMap.ForAllMembers((expression) => expression.MapFrom(source =>
source.ContainsKey(expression.DestinationMember.Name.Substring(0, 1).ToLower()
+ expression.DestinationMember.Name.Substring(1))
? source[expression.DestinationMember.Name.Substring(0, 1).ToLower()
+ expression.DestinationMember.Name.Substring(1)] : null
));
});
For mapping a dynamic/expando object that is camel case to an type with pascal case members you could use ForAllMembers on the the explicit map configuration. Possible use case: json payloads to DTO.
In comparison to the other answer (which also works) this approach allows you to continue to benefit and use all the other map features and configuration.
I'm trying to map a Dictionary<string, object> where properties are keyed with Camel casing to a C# object with Pascal casing:
var id = Guid.NewGuid();
var dto = new Dictionary<string, object> {
{ "id", id.ToString() },
{ "value", "some value" },
};
var mapper = new MapperConfiguration(cfg => { }).CreateMapper();
var result = mapper.Map<TestClass>(dto);
var expected = new TestClass {
Id = id,
Value = "some value",
};
result.Should().BeEquivalentTo(expected);
If I put the dictionary keys as PascalCase, it works out of the box. I'm guessing I need a custom INamingConvention, but the documentation isn't quite clear about what it should be like...
Can someone help me with that?
using Automapper v7.0.1
Yes, I could do it the long way and map it manually, I know
Edit
There is a closed issue in AutoMapper's github repo: https://github.com/AutoMapper/AutoMapper/issues/2903
I need generic way to filter IQueryable data and filters are populated as dictionary. I have already created method like this.
public static IEnumerable<T> CustomApplyFilter<T>(this IQueryable<T> source, Dictionary<string, string> filterBy)
{
foreach (var key in filterBy.Keys)
{
source.Where(m => m.GetType().GetProperty(key).GetValue(m, null).Equals(filterBy[key]));
}
return source.ToList();
}
But its always returning same result.
please find the caller
Dictionary<string, string> dtFilter = new Dictionary<string, string>();
dtFilter.Add("Id", "2");
var res = context.Set<MyEntity>().CustomApplyFilter<MyEntity>(dtFilter);
The Where extension method does not change the content of the IQueryable it is applied to. The return value of the method should be used:
public static IEnumerable<T> CustomApplyFilter<T>(this IQueryable<T> source, Dictionary<string, string> filterBy)
{
foreach (var key in filterBy.Keys)
{
source = source.Where(m => m.GetType().GetProperty(key).GetValue(m, null).Equals(filterBy[key]));
}
return source.ToList();
}
UPDATE:
I should have noticed it, my answer so far was applicable to LINQ to Objects only. When using LINQ to Entities, however, there are certain restrictions; only expression that can be converted to an SQL query can be used. Getting properties through reflection is not such an expression obviously.
When this is the case, one possible solution would be to build the ExpressionTree manually.
public static IEnumerable<T> CustomApplyFilter<T>(this IQueryable<T> source, Dictionary<string, string> filterBy)
{
foreach (var key in filterBy.Keys)
{
var paramExpr = Expression.Parameter(typeof(T), key);
var keyPropExpr = Expression.Property(paramExpr, key);
var eqExpr = Expression.Equal(keyPropExpr, Expression.Constant(filterBy[key]));
var condExpr = Expression.Lambda<Func<T, bool>>(eqExpr, paramExpr);
source = source.Where(condExpr);
}
return source.ToList();
}
UPDATE2:
With the comment #Venkatesh Kumar given below, it is apparent that when the underlying type of the field provided is not of type string, this solution fails (with the error message : The binary operator Equal is not defined for the types 'System.Int64' and 'System.String').
One possible way to tackle this problem would be to have a dictionary of types and delegates to use for each such property.
Since this is a static method (an extension method which has to be static), declaring a static Dictionary in class scope would be reasonable:
Let's assume the name of the class in which CustomApplyFilter is declared is SOFExtensions:
internal static class SOFExtensions
{
private static Dictionary<Type, Func<string, object>> lookup = new Dictionary<Type, Func<string, object>>();
static SOFExtensions()
{
lookup.Add(typeof(string), x => { return x; });
lookup.Add(typeof(long), x => { return long.Parse(x); });
lookup.Add(typeof(int), x => { return int.Parse(x); });
lookup.Add(typeof(double), x => { return double.Parse(x); });
}
public static IEnumerable<T> CustomApplyFilter<T>(this IQueryable<T> source, Dictionary<string, string> filterBy)
{
foreach (var key in filterBy.Keys)
{
var paramExpr = Expression.Parameter(typeof(T), key);
var keyPropExpr = Expression.Property(paramExpr, key);
if (!lookup.ContainsKey(keyPropExpr.Type))
throw new Exception("Unknown type : " + keyPropExpr.Type.ToString());
var typeDelegate = lookup[keyPropExpr.Type];
var constantExp = typeDelegate(filterBy[key]);
var eqExpr = Expression.Equal(keyPropExpr, Expression.Constant(constantExp));
var condExpr = Expression.Lambda<Func<T, bool>>(eqExpr, paramExpr);
source = source.Where(condExpr);
}
return source.ToList();
}
}
Other types and proper delegates for them should be added to the lookup Dictionary as required.
I have an example like this :
class Fields
{
string ContactOneName{get;set;}
string ContactOnePhone{get;set;}
string ContactOneSpouseName{get;set;}
string ContactOneSpousePhone{get;set;}
}
And I would like to map to a model like this:
class Contacts
{
Contact ContactOne {get;set;}
Contact ContactOneSpouse {get;set;}
}
class Contact
{
string Name {get;set;}
string Phone {get;set;}
}
There are lots of fields and I don't want to write a mapping for each field.
Is this possible?
If so how?
NB: This question is almost a duplicate of AutoMapper unflattening complex objects of same type but I want a solution NOT manually mapping everything, because in that case it is not worth using automapper.
You can take this and add:
public static IMappingExpression<TSource, TDestination> ForAllMembers<TSource, TDestination, TMember>(
this IMappingExpression<TSource, TDestination> mapping,
Action<IMemberConfigurationExpression<TSource, TDestination, TMember>> opt)
{
var memberType = typeof(TMember);
var destinationType = typeof(TDestination);
foreach(var prop in destinationType.GetProperties().Where(prop => prop.PropertyType.Equals(memberType)))
{
var parameter = Expression.Parameter(destinationType);
var destinationMember = Expression.Lambda<Func<TDestination, TMember>>(Expression.Property(parameter, prop), parameter);
mapping.ForMember(destinationMember, opt);
}
return mapping;
}
Then you can configure the mapping as follows:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Fields, Contacts>().ForAllMembers<Fields, Contacts, Contact>(x => { x.Unflatten(); });
});
I am attempted to use AutoMapper to map a DataServiceCollection to a list of strings and also create the reverse mapping. Any ideas on how to map a specialized collection like this to another?
Mapper.CreateMap<DataServiceCollection<LocationCountyValue>, List<string>>();
You can create a custom type converter:
public class DataServiceCollectionToStringList : ITypeConverter<DataServiceCollection<LocationCountyValue>, List<string>> {
public List<string> Convert(ResolutionContext context) {
var sourceValue = (DataServiceCollection<LocationCountyValue>) context.SourceValue;
/* Your custom mapping here. */
}
}
Then create the map with ConvertUsing:
Mapper.CreateMap<DataServiceCollection<LocationCountyValue>, List<string>>()
.ConvertUsing<DataServiceCollectionToStringList>();
Thanks to Thiago Sa I created the mapping in both directions like so:
Mapper.CreateMap<DataServiceCollection<CountyValue>, List<string>>()
.ConvertUsing((src) => { return src.Select(c => c.Value).ToList(); });
Mapper.CreateMap<List<string>, DataServiceCollection<CountyValue>>()
.ConvertUsing((src) =>
{
return new DataServiceCollection<CountyValue>(
src.Select(c => new CountyValue() { Value = c }));
});