Map object properties to dictionary on automapper - automapper

I am trying to map an object to a dictionary in a way that each property will be a dictionary item.
Object with id and name -> dictionary with two items containing property name and value.
I know it is a simple thing, but I was not able to find a solution for it. Maybe it is something I am not understanding...
I receive the following error:
Unmapped members were found. Review the types and members below.
Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type
For no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters
=======================================================================================================================================================================================================================================================================
Book -> Dictionary`2 (Destination member list)
Book -> System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.String, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]] (Destination member list)
Unmapped properties:
Keys
Values
Item
In the following code you can see my implementation:
using AutoMapper;
var book = new Book()
{
Id = 1,
Name = "A"
};
IMapper mapper = new Mapper(new MapperConfiguration(
config => config.CreateMap<Book, Dictionary<string, string>>()
.ConstructUsing((source, dest) => new Dictionary<string, string>()
{
{ "id", source.Id.ToString() },
{"name", source.Name}
})));
mapper.ConfigurationProvider.AssertConfigurationIsValid();
var bookData = new Dictionary<string, string>();
mapper.Map(book, bookData);
public class Book
{
public int Id { get; set; }
public string Name { get; set; }
}

As #Lucian mentioned in a comment, Automapper has built-in methods to convert object from/to dynamic, Dictionary<string, object>.
However, I think that there is an issue if you implement a converter to Dictionary<string, string>.
Hence, it is better to build a custom implementation for the conversion as below:
using System.Reflection;
using Newtonsoft.Json;
public static class DictionaryExtensions
{
public static Dictionary<string, string> ToDictionary<T>(this T src) where T : new()
{
Dictionary<string, string> result = new ();
PropertyInfo[] properties = typeof(T).GetProperties();
foreach (PropertyInfo prop in properties)
{
result.Add(prop.Name, prop.PropertyType == typeof(string)
? prop.GetValue(src)?.ToString()
: JsonConvert.SerializeObject(prop.GetValue(src)));
}
return result;
}
}
For caller:
var bookData = book.ToDictionary();
Demo # .NET Fiddle

Related

I need generic way to filter IQueryable data and filters are populated as dictionary

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.

Conflicts in AutoMapper and AutoFixture

I have 2 classes, Class1 should be mapped to Class2. I do mapping with AutoMapper. I'd like to test my configuration of the mapper and for this purposes I'm using AutoFixture. Source class Class1 has property of type IList<>, destination class Class2 has a similar property but of type IEnumerable<>. To simplify test preparation I'm using AutoFixture (with AutoMoqCustomization) to initialize both source and destination objects. But after initializing property of type IEnumerable<> with AutoFixture, AutoMapper can't map the property.
Error text:
Error mapping types.
Mapping types: Class1 -> Class2 ConsoleApplication1.Class1 ->
ConsoleApplication1.Class2
Type Map configuration: Class1 -> Class2 ConsoleApplication1.Class1 ->
ConsoleApplication1.Class2
Property: Items
Could anybody help me to configure either AutoMapper or AutoFixture to make the mapping work? As a workaround I can assign null to the destination property, but I do not want to do this in the each test.
Simplified example of code:
public class AutoMapperTests
{
public static void TestCollectionsProperty()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<ItemClass1, ItemClass2>();
cfg.CreateMap<Class1, Class2>();
});
var src = new Class1();
src.Items = new List<ItemClass1>()
{
new ItemClass1() { Text = "111" },
new ItemClass1() { Text = "222" }
};
var fixture = new Fixture();
var dst = fixture.Create<Class2>();
Mapper.Map(src, dst); //Error at this line of code
}
}
public class Class1
{
public IList<ItemClass1> Items { get; set; }
}
public class Class2
{
public IEnumerable<ItemClass2> Items { get; set; }
}
public class ItemClass1
{
public string Text { get; set; }
}
public class ItemClass2
{
public string Text { get; set; }
}
It's not really an AutoFixture issue per se. You can reproduce it without AutoFixture by instead creating dst like this:
var dst = new Class2();
dst.Items = Enumerable.Range(0, 1).Select(_ => new ItemClass2());
This will produce a similar error message:
Unable to cast object of type 'WhereSelectEnumerableIterator2[System.Int32,Ploeh.StackOverflow.Q45437098.ItemClass2]' to type 'System.Collections.Generic.IList1[Ploeh.StackOverflow.Q45437098.ItemClass2]'
That ought to be fairly self-explanatory: WhereSelectEnumerableIterator<int, ItemClass2> doesn't implement IList<ItemClass2>. AutoMapper attempts to make that cast, and fails.
The simplest fix is probably to avoid populating dst:
var dst = new Class2();
If you must use AutoFixture for this, you can do it like this:
var dst = fixture.Build<Class2>().OmitAutoProperties().Create();
Unless the Class2 constructor does something complex, however, I don't see the point of using AutoFixture in that scenario.
If, on the other hand, you do need dst to be populated, you just need to ensure that dst.Items is convertible to IList<ItemClass2>. One way to do that would be like this:
var dst = fixture.Create<Class2>();
dst.Items = dst.Items.ToList();
You could create a Customization to make sure that this happens automatically, but if you need help with that, please ask a new question (if you don't find one that already answers that question).
Here is a working example for your problem. As #Mark Seemann already told, Mapper.CreateMap has been deprecated, so this example is using the new structure.
Mapper.Initialize(cfg =>
{
cfg.CreateMap<ItemClass1, ItemClass2>();
cfg.CreateMap<Class1, Class2>();
});
var src = new Class1();
src.Items = new List<ItemClass1>()
{
new ItemClass1() { Text = "111" },
new ItemClass1() { Text = "222" }
};
var dest = Mapper.Map<Class1, Class2>(src);
AM requires IList because you're mapping to an existing list and that works by calling IList.Add.

cannot be able to use "as" key word in DynamicTableEntity (Azure Table)

I have an Azure table where I have inserted heterogeneous entities. After the retrieval, I want to convert them to some specific type using "as". I tried to do this, but it threw the following error:
Cannot be able to convert DynamicTableEntity to TestingEntity Via reference conversion, boxing conversion, unboxing conversion, wrapping conversion or null type conversion.
Is there any way I can convert my entities to a particular type?
My code is as follows:
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));
// Create the table client.
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
CloudTable table = tableClient.GetTableReference("TestingWithTableDatetime");
// Create the table if it doesn't exist.
table.CreateIfNotExists();
TableQuery<DynamicTableEntity> entityQuery =
new TableQuery<DynamicTableEntity>();
var employees = table.ExecuteQuery(entityQuery);
IEnumerable<DynamicTableEntity> entities = table.ExecuteQuery(entityQuery);
foreach (var e in entities)
{
EntityProperty entityTypeProperty;
if (e.Properties.TryGetValue("EntityType", out entityTypeProperty))
{
if (entityTypeProperty.StringValue == "SampleEntity1")
{
//Cannot be able to Use as
var TestingWithTableDatetime = e as SampleEntity1;
}
if (entityTypeProperty.StringValue == "SampleEntity2")
{
// Use entityTypeProperty, RowKey, PartitionKey, Etag, and Timestamp
}
if (entityTypeProperty.StringValue == "SampleEntity3")
{
// Use entityTypeProperty, RowKey, PartitionKey, Etag, and Timestamp
}
}
}
Class definition for Sample1
public class Sample1 : TableEntity
{
public Sample1(string pk, string rk)
{
this.PartitionKey = pk;
this.RowKey = rk;
EntityType = "MonitoringResources";
}
public string EntityType { get; set; }
public Sample1()
{
}
}
Things I have tried.I have created a class as Testing and in that I inherited Table entity.Then Testing is inherited by sample1 as follow
Testing Class definition
public class testing : TableEntity
{
public testing(string pk, string rk)
{
this.PartitionKey = pk;
this.RowKey = rk; //MetricKey
}
public string EntityType { get; set; }
public testing()
{
}
}
modified Class sample1:
public class sample1 : testing
{
public sample1(string pk, string rk) : base(pk, rk)
{
EntityType = "sample1";
}
public sample1()
{
}
}
In this i didnt get any error but
when I am converting it to sample1 by using "as" it returns as null.
Finally I ended with creating some helper.
public static class AzureManager
{
/// <summary>
/// Converts a dynamic table entity to .NET Object
/// </summary>
/// <typeparam name="TOutput">Desired Object Type</typeparam>
/// <param name="entity">Dynamic table Entity</param>
/// <returns>Output Object</returns>
public static TOutput ConvertTo<TOutput>(DynamicTableEntity entity)
{
return ConvertTo<TOutput>(entity.Properties, entity.PartitionKey, entity.RowKey);
}
/// <summary>
/// Convert a Dynamic Table Entity to A POCO .NET Object.
/// </summary>
/// <typeparam name="TOutput">Desired Object Types</typeparam>
/// <param name="properties">Dictionary of Table Entity</param>
/// <returns>.NET object</returns>
public static TOutput ConvertTo<TOutput>(IDictionary<string, EntityProperty> properties, string partitionKey, string rowKey)
{
var jobject = new JObject();
properties.Add("PartitionKey", new EntityProperty(partitionKey));
properties.Add("RowKey", new EntityProperty(rowKey));
foreach (var property in properties)
{
WriteToJObject(jobject, property);
}
return jobject.ToObject<TOutput>();
}
public static void WriteToJObject(JObject jObject, KeyValuePair<string, EntityProperty> property)
{
switch (property.Value.PropertyType)
{
case EdmType.Binary:
jObject.Add(property.Key, new JValue(property.Value.BinaryValue));
return;
case EdmType.Boolean:
jObject.Add(property.Key, new JValue(property.Value.BooleanValue));
return;
case EdmType.DateTime:
jObject.Add(property.Key, new JValue(property.Value.DateTime));
return;
case EdmType.Double:
jObject.Add(property.Key, new JValue(property.Value.DoubleValue));
return;
case EdmType.Guid:
jObject.Add(property.Key, new JValue(property.Value.GuidValue));
return;
case EdmType.Int32:
jObject.Add(property.Key, new JValue(property.Value.Int32Value));
return;
case EdmType.Int64:
jObject.Add(property.Key, new JValue(property.Value.Int64Value));
return;
case EdmType.String:
jObject.Add(property.Key, new JValue(property.Value.StringValue));
return;
default:
return;
}
}
}
the above one works for me.
var obj= AzureManager.ConvertTo<Sample1>(e);
If you find any other way.Please suggest.
Here is an alternative and much simpler solution for you that is natively supported by Azure Storage SDK version > 8.0.0. You do not even need to write any transformation / conversion code :)
Have a look at:
TableEntity.Flatten method: https://msdn.microsoft.com/en-us/library/azure/mt775434.aspx
TableEntity.ConvertBack method: https://msdn.microsoft.com/en-us/library/azure/mt775432.aspx
These methods are provided by the SDK as static, standalone helper methods. Flatten method will convert your entities to a flat dictionary of entity properties where you can simply assign a partition key and row key, create a dynamictableentity from the flat dictionary and write to azure table storage.
When you want to read the entity back, read it as dynamic table entity and pass the property dictionary of the returned dynamic table entity to TableEntity.ConvertBack method. Just tell it which type of object you want the method to convert the property dictionary into, via its generic type parameter and it will do the conversion for you.
I originally implemented these api s as nuget packages and now they are integrated into azure storage sdk. If you want to read a bit more about how they work you can see the article I wrote originally about the nuget packages here:
https://doguarslan.wordpress.com/2016/02/03/writing-complex-objects-to-azure-table-storage/
Is there any way I can convert my entities to a particular type?
We could use DynamicTableEntityConverter to do that.
According to your code, we could use the following code to covert DynamicTableEntity to Sample1
var TestingWithTableDatetime = DynamicTableEntityConverter.ConvertToPOCO<Sample1>(e);

Forgetting to map classes with AutoMapper

The application I'm working on has several places where we use AutoMapper to map entities.
The problem is if I had a model entity from one side to the other of the project, many times I forget to add the mapping for the new entity (I just need a copy paste from other entities), ending up that the solution compiles and I get no exception.
It just launches the application without full functionality and no debugging messages, which makes difficult to figure out what I've missed.
Is there any way to force the compiler at compile time to give me an error in case I forget to do a mapping?
AFAIK, there isn't a possibility to force compile-time checking for Automapper.
Nevertheless, there is a possibility to verify the correctness of your mappings:
After you've defined all your mappings, call the AssertConfigurationIsValid method which will throws an AutoMapperConfigurationException exception if the defined mappings are broken.
You can make this a part of your unit or integration test suite.
I had the same problem and decided to solve it by wrapping up AutoMapper. For each source-destination map I provide a method that I create after I've added it to my AutoMapper profile.
This may take away some of the ease of implementing AutoMapper but I find the compile time checking worth it.
public class MyType {
public string SomeProperty { get;set; }
}
public class MyOtherType {
public string SomeProperty { get;set; }
}
public class MyAlternateType {
public string AlternateProperty {get;set;}
}
public class AutoMapperProfile : Profile {
public AutoMapperProfile() {
CreateMap<MyType, MyOtherType>();
CreateMap<MyAlternateType, MyOtherType>()
.ForMember(ot => ot.SomeProperty, options => options.MapFrom(at => at.AlternateProperty));
}
}
public interface IMyMappingProvider {
// Uncomment below for Queryable Extensions
//IQueryable<TDestination> ProjectTo<TSource, TDestination>(IQueryable<TSource> source, params Expression<Func<TDestination, object>>[] membersToExpand);
//IQueryable<TDestination> ProjectTo<TSource, TDestination>(IQueryable<TSource> source, IDictionary<string, object> parameters, params string[] membersToExpand);
/*
* Add your mapping declarations below
*/
MyOtherType MapToMyOtherType(MyType source);
MyOtherType MapToMyOtherType(MyAlternateType source);
}
public class MyMappingProvider : IMyMappingProvider {
private IMapper Mapper { get; set; }
public MyMappingProvider(IMapper mapper) {
Mapper = mapper;
}
/* Uncomment this for Queryable Extensions
public IQueryable<TDestination> ProjectTo<TSource, TDestination>(IQueryable<TSource> source, params Expression<Func<TDestination, object>>[] membersToExpand) {
return new ProjectionExpression(source, Mapper.ConfigurationProvider.ExpressionBuilder).To<TDestination>(null, membersToExpand);
}
public IQueryable<TDestination> ProjectTo<TSource, TDestination>(IQueryable<TSource> source, IDictionary<string, object> parameters, params string[] membersToExpand) {
return new ProjectionExpression(source, Mapper.ConfigurationProvider.ExpressionBuilder).To<TDestination>(parameters, membersToExpand);
}
*/
/*
* Implement your mapping methods below
*/
public MyOtherType MapToMyOtherType(MyType source) {
return Mapper.Map<MyType, MyOtherType>(source);
}
public MyOtherType MapToMyOtherType(MyAlternateType source) {
return Mapper.Map<MyAlternateType, MyOtherType>(source);
}
}
If you are using the AutoMapper's Queryable extensions you can add the following class and uncomment the Queryable Extensions code above.
public static class QueryableExtensions {
/*
* Implement your extension methods below
*/
public static IQueryable<MyOtherType> ProjectToMyOtherType(this IQueryable<MyType> source, IMyMappingProvider mapper, params Expression<Func<MyOtherType, object>>[] membersToExpand)
{
return mapper.ProjectTo<MyType, MyOtherType>(source, membersToExpand);
}
public static IQueryable<MyOtherType> ProjectToMyOtherType(this IQueryable<MyAlternateType> source, IMyMappingProvider mapper, params Expression<Func<MyOtherType, object>>[] membersToExpand)
{
return mapper.ProjectTo<MyAlternateType, MyOtherType>(source, membersToExpand);
}
}
Tested with AutoMapper 6.1.1 using LinqPad:
var autoMapperConfig = new MapperConfiguration(cfg => { cfg.AddProfile(new AutoMapperProfile()); });
IMyMappingProvider mapper = new MyMappingProvider(autoMapperConfig.CreateMapper());
var myTypes = new List<MyType>()
{
new MyType() {SomeProperty = "Test1"},
new MyType() {SomeProperty = "Test2"},
new MyType() {SomeProperty = "Test3"}
};
myTypes.AsQueryable().ProjectToMyOtherType(mapper).Dump();
var myAlternateTypes = new List<MyAlternateType>()
{
new MyAlternateType() {AlternateProperty = "AlternateTest1"},
new MyAlternateType() {AlternateProperty = "AlternateTest2"},
new MyAlternateType() {AlternateProperty = "AlternateTest3"}
};
myAlternateTypes.AsQueryable().ProjectToMyOtherType(mapper).Dump();
mapper.MapToMyOtherType(myTypes[0]).Dump();
As #serge.karalenka said, don't forget to still test your mapping configuration by calling AssertConfigurationIsValid().

Adding new dynamic properties

we read in msdn we "Adding new dynamic properties" by using DynamicObject Class
i write a following program
public class DemoDynamicObject : DynamicObject
{
}
class Program
{
public static void Main()
{
dynamic dd = new DemoDynamicObject();
dd.FirstName = "abc";
}
}
But when i run this program it gives runtime error :'DemoDynamicObject' does not contain a definition for 'FirstName'
if we adding dynamic property by using DynamicObject Class then why it can give this error
can anyone tell me reason and solution?
When using DynamicObject as your base class, you should provide specific overrides to TryGetMember and TrySetMember to keep track of the dynamic properties you are creating (based on the DynamicObject MSDN documentation):
class DemoDynamicObject: DynamicObject
{
Dictionary<string, object> dictionary
= new Dictionary<string, object>();
public override bool TryGetMember(
GetMemberBinder binder, out object result)
{
string name = binder.Name;
return dictionary.TryGetValue(name, out result);
}
public override bool TrySetMember(
SetMemberBinder binder, object value)
{
dictionary[binder.Name] = value;
return true;
}
}
If you just want to have a dynamic object that you can add properties to, you can simply use an ExpandoObject instance, and skip the custom class inheriting from DynamicObject.

Resources