49

I've started to use https://github.com/robconery/massive for a project, I wonder if there is any mapping tool that allows support for Dynamic to static type mapping?

I've used AutoMapper previously, does AutoMapper support this?

I am aware of the DynamicMap function from AutoMapper, however I believe this function is for running maps without creating the Map first. In my example below it does not work.

dynamic curUser = users.GetSingleUser(UserID);   
var retUser = Mapper.DynamicMap<UserModel>(curUser);
users.GetSingleUser(UserID); // returns a dynamic object
abatishchev
  • 98,240
  • 88
  • 296
  • 433
LiamB
  • 18,243
  • 19
  • 75
  • 116
  • See [this q](http://stackoverflow.com/questions/19529178/recursively-mapping-expandoobject) for a more complete answer. – nawfal Jul 19 '14 at 19:59
  • @LiamB, "does AutoMapper support this?" - you mean automatic mapping between properties with equal/similar names? Or your mapping is more complex? – Leonid Vasilev Jul 03 '15 at 17:41
  • @LeonidVasilyev Sorry - I'm not sure, this question is 4 years old! :) – LiamB Jul 03 '15 at 18:51
  • 1
    @LiamB, wow, indeed it is:) – Leonid Vasilev Jul 03 '15 at 18:59
  • I dont get it... As far as I see it the code above is criminal. If I saw dynamics used in such a way I would have serious questions about said programmers abilities and the quality of the library used. I realize there might be some domain specific knowledge that isnt clear in the question but still.... C# != JavaScript. Why would any ORM return a dynamic? Yuck! – Maxim Gershkovich Jul 03 '15 at 20:22
  • @MaximGershkovich This question and code is from 4 years ago - I havent used AutoMapper in a while now. However dynamics are extremely powerful and just because C# isnt JS doesn't mean the use of dynamics is bad. – LiamB Jul 04 '15 at 10:01

4 Answers4

76

AutoMapper 4.2.0 now supports Dynamic/expando/dictionary mapping

With this feature you can map to your expando objects to static types:

dynamic CurUser = _users.GetSingleUser(UserID);   
var config = new MapperConfiguration(cfg => { });
var mapper = config.CreateMapper();

var retUser = mapper.Map<UserModel>(CurUser);

Old versions of AutoMapper do not support this (Massive internally uses ExpandoObject which doesn't provide which properties it has), and you are right Mapper.DynamicMap is for mapping without creating mapping configuration.

Actually it's not hard to write yourself a mapper if you just want simple mapping:

public static class DynamicToStatic
{
    public static T ToStatic<T>(object expando)
    {
        var entity = Activator.CreateInstance<T>();

        //ExpandoObject implements dictionary
        var properties = expando as IDictionary<string, object>; 

        if (properties == null)
            return entity;

        foreach (var entry in properties)
        {
            var propertyInfo = entity.GetType().GetProperty(entry.Key);
            if(propertyInfo!=null)
                propertyInfo.SetValue(entity, entry.Value, null);
        }
        return entity;
    }
}

dynamic CurUser = _users.GetSingleUser(UserID);   
var retUser = DynamicToStatic.ToStatic<UserModel>(CurUser);
nemesv
  • 138,284
  • 16
  • 416
  • 359
  • I have been struggling with this for hours this afternoon +1. Thanks – abarr Nov 06 '11 at 11:48
  • 4
    @nemesv "[...]ExpandoObject which doesn't provide which properties it has)[...]" <--- ExpandoObject implements `IDictionary` so you can cast to this interface and use `ContainsKey`. – Matías Fidemraizer Apr 08 '14 at 13:28
  • Just in case somebody has issues finding properties. GetProperty(...) is case sensitive by default. Use the following flags to make it not case sensitive .GetProperty(entry.Key, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance); – Miguel Jul 13 '21 at 13:23
  • Using empty config doesn't work with collections. – gsscoder Oct 27 '21 at 14:17
17

Try Slapper.AutoMapper https://github.com/randyburden/Slapper.AutoMapper

Slapper.AutoMapper maps dynamic data to static types

It works for both dynamic and Dictionary<A, B> which is awesome.

Here's an example (taken from the URL above) showing how easily it works with Dictionary:

public class Person
{
    public int Id;
    public string FirstName;
    public string LastName;
}

[Test]
public void CanMapMatchingFieldNamesWithEase()
{
    // Arrange
    var dictionary = new Dictionary<string, object>
                            {
                                { "Id", 1 },
                                { "FirstName", "Clark" },
                                { "LastName", "Kent" }
                            };

    // Act
    var person = Slapper.AutoMapper.Map<Person>( dictionary );

    // Assert
    Assert.NotNull( person );
    Assert.That( person.Id == 1 );
    Assert.That( person.FirstName == "Clark" );
    Assert.That( person.LastName == "Kent" );
}
Liam
  • 27,717
  • 28
  • 128
  • 190
Tod Thomson
  • 4,773
  • 2
  • 33
  • 33
  • This works with deep hierarchies, but aah.. the `_` convention is a bummer.. :( – nawfal Jul 19 '14 at 09:07
  • 1
    nooooooooo, I didn't mean the name of your method. I meant, this is one library that will work with deep hierarchies in class structure (complex class structure unlike the example in your answer) unlike the accepted answer, but it requires a certain "naming convention" to work. See the link for an example. – nawfal Jul 21 '14 at 06:18
  • 1
    Apologies I should have paid more attention! :) I agree it would be nicer if the sub-dynamic-types were properties of the parent so you get Foo.Bar.Baz instead of Foo_Bar_Baz... – Tod Thomson Jul 21 '14 at 06:46
  • 2
    @nawfal The "_" convention can be customised, e.g. `Slapper.AutoMapper.Configuration.IdentifierConventions.Add( type => type.Name + "_Id" );`, see http://randyburden.com/Slapper.AutoMapper/. – Contango May 06 '15 at 13:57
3

Assuming framework you use returns ExpandoObject you can achieve some sort of dynamic mapping using AutoMapper:

Mapper.CreateMap<ExpandoObject, UserModel>()
    .ForAllMembers((options) => options.ResolveUsing((resolution) =>
        {
            var dictionary =  (IDictionary<string, object>) resolution.Context.SourceValue;
            return dictionary[resolution.Context.MemberName];
        }));
...
dynamic CurUser = _users.GetSingleUser(UserID);   
var retUser = Mapper.Map<UserModel>(CurUser);

You can create any sort of complex mapping using ConstructUsing methods..

Leonid Vasilev
  • 11,910
  • 4
  • 36
  • 50
0

Single object:

Mapper.Map<Product>(dynamicProduct);

List:

Mapper.Map<List<Product>>(dynamicListOfProducts);

Example (line 71): https://github.com/AutoMapper/AutoMapper/blob/master/src/UnitTests/DynamicMapping.cs

tedi
  • 6,350
  • 5
  • 52
  • 67