Anyone good with the latest version of Auto Mapper c# 5.0+?

HumblePie

Lifer
Oct 30, 2000
14,665
440
126
So here is my issue in a small break down way. Collections within an object aren't mapping.

Code:
public partial class DBObjectOne
{
   public int intValue { get; set; };
   public string stringValue { get; set; };
   public DBObjectTwo reference { get; set; };
}

public partial class DBObjectTwo
{
   public string nameValue { get; set; };
   public virtual ICollection<DBObjectOne> ListOfObjects { get; set; };
}

I am mapping those to the following made model classes

Code:
public class DBObjectOneModel
{
   public int intValue { get; set; };
   public string stringValue { get; set; };
   public DBObjectTwoModel reference { get; set; };
}

public class DBObjectTwoModel
{
   public string nameValue { get; set; };
   public ICollection<DBObjectOneModel> ListOfObjectModels { get; set; };
}

I am trying to setup my App_Start class to map those out properly

Code:
public static class AutoMapperConfig
{
   public static MapperConfiguration MapperConfig;
   public static void RegisterMappings()
   {
      MapperConfig = MapperConfiguration( cfg => {
         cfg.CreateMap<DBObjectOne, DBObjectOneModel>();
         cfg.CreateMap<DBObjectOneModel, DBObjectOne>();
         cfg.CreateMap<DBObjectTwo, DBObjectTwoModel>();
         cfg.CreateMap<DBObjectTwoModel, DBObjectTwo>();
         });
   }
}

Of course when I instantiate and try to call the map I get a stack overflow since the collection of DBObjectOne, called ListOfObjects, doesn't map to the model class, ListOfObjectModels. When I googled the fix, I tried several of the "answers" I found but nothing seemed to work. Either I'd get an argument except or stackoverflow exception. Basically these are the answers I tried.

Code:
cfg.CreateMap<DbObjectTwo, DBObjectTwoModel().ForMember(dest => dest.ListOfObjectModels, opt => opt.MapFrom(src => src.ListOfObjects));

I've also tried

Code:
cfg.CreateMap<DbObjectTwo, DBObjectTwoModel().ForMember(dest => dest.ListOfObjectModels, opt => opt.MapFrom(src => Mapper.Map<IEnumerable<ListOfObjectModels>>(src.ListOfObjects)));

I tried various ways to get those lists to map internally to each other, but am having no luck with the various "solutions" I've found. The problem is most of the solutions are for versions previous to 4.2 where the Mapper class was an already instantiated static class and worked a bit differently than the current version where you instantiated a version of the IMapper class now. I just make that static and use it through injection in other classes. Usually I would work my objects to not need mapping back and forth and thus do what I needed through reflection instead in a single loops. But since I'm doing several maps back and forth throughout this application for various reasons I am using the AutoMapper package as it is a bit better in overhead and performance in that regard that what I could whip up in time for this current project. Still, this annoyance is stumping me and the official documentation doesn't seem to be much of a help either at this moment.

Any ideas here?
 
Last edited:
  • Like
Reactions: shortylickens

HumblePie

Lifer
Oct 30, 2000
14,665
440
126
well, looks like there was a jump update recently to 5.0 and a few more releases after that. Switched out to 5.0.2 now and still having similar problems. Wondering if I should try TinyMapper instead.

After some testing, it's that reference back to the original that is throwing this off. This is the foreign key constraint object being generated back by entity framework. If removed, the the previous solutions I try work just fine. But not seeing any solutions related directly to the problem I have at hand.
 
Last edited:
  • Like
Reactions: shortylickens

HumblePie

Lifer
Oct 30, 2000
14,665
440
126
Update, figured it out. Had to use the PreserveReferences method on my config. It should have been.

Code:
cfg.CreateMap<DbObjectTwo, DBObjectTwoModel().ForMember(dest => dest.ListOfObjectModels, opt => opt.MapFrom(src => src.ListOfObjects)).PreserveReferences();

Since I have references to my original objects inside the nested list of objects. That preserves the original mappings for that reference. For anyone that wonders it is a massive pain to figure out.
 

HumblePie

Lifer
Oct 30, 2000
14,665
440
126
The 5.0 upgrade guide is worth a skim, there were some fairly significant changes (this among them): https://github.com/AutoMapper/AutoMapper/wiki/5.0-Upgrade-Guide

Purely from a design POV I wouldn't recommend this kind of mapping - I'd use DTOs or business objects that hide the internal database structure.

Making the DTO's requires mappings with EF objects :p that's basically what I was doing with showing a very generic semi example of the problem I was having. I wished I had found that page earlier and realized it was the circular reference that was tripping me up as that page you linked explained it after I had found it by happenstance. I've done basic reflection for smaller objects in the past, but they are wanting a bit more expose in the business layer to the data objects so I decided to map things up with automapper for this project which I hadn't used in awhile.
 

Merad

Platinum Member
May 31, 2010
2,586
19
81
Making the DTO's requires mappings with EF objects

Yeah, but you don't have to include all of the navigation properties in mapping. I usually mark them with AutoMapper's ignore attribute (IgnoreMap? NotMapped? I forget) so they have to be explicitly included if you want them.
 

HumblePie

Lifer
Oct 30, 2000
14,665
440
126
Yeah, but you don't have to include all of the navigation properties in mapping. I usually mark them with AutoMapper's ignore attribute (IgnoreMap? NotMapped? I forget) so they have to be explicitly included if you want them.
True, but I'm trying to make to make it all work as generically as possible for a client that is suppose to have less than capable developers maintain this after I leave. They may not know the mapping piece as well and what to pull out of the DTOs. So trying to make it work regardless of what is taken out or left in. Speaking of which, got all that wrapped up today so it doesn't matter so much if the base entity objects change or they make changes to the DTOs as it all works regardless if they don't majorly change all the names. Just incase, I wrote a Json override that can be used and modified on the fly in the event the DTOs and DBO's get radically changed and they don't have a programmer with a clue to reconcile those changes. The company basically only has SQL guys employed right now as perm positions.
 

HumblePie

Lifer
Oct 30, 2000
14,665
440
126
So having a fun issue with AutoMapper 5.0 in trying to either setup it up for dependency injection or at the very least use it as a singleton. 4.2 switched the API from static to non static in usage, but in 5.0 they removed the profile services which allowed the automapper to be added to the windsor services. So I'm trying to figure out a way to use Unity for dependency injection at least for now to de-couple my mapper from all my other classes. Here is basically what I got going on.


AutpMapperConfig.cs
Code:
public static class AutoMapperConfig
{
public static MapperConfiguration MapperConfiguration;


public static void RegisterMappings()
{
     MapperConfiguration = new MapperConfiguration(cfg => {
          cfg.CreateMap<DTO, DAO>(MemberList.source).PreserveReferences().ReverseMap();
     });

#if DEBUG
     AutoMapperConfig.MapperConfiguration.AssertConfigurationIsValid();
#endif
  }
}



Global.asax
Code:
protected void Application_Start()
{
    AutoMapperConfig.RegisterMappings();
}

And for where I need automapper in the rest of my code I am currently using it like this.

MyRepositoryClass.cs
Code:
namespace Repositories
{
  public class MyRepositoryClass : IMyRepositoryClass
  {
    private static readonly IMapper Mapper = AutoMapperConfig.MapperConfiguration.CreateMapper();

    public void GetDTO(int index, out Result result)
    {
      result = new Result();
 
      try
      {
        using (_dbContext)
        {
           var DAO = _dbContext.MyTable.Find(index);
           result.DynamicObject = Mapper.Map<DTO>(DAO);
           result.Success = true;
        }
      }
      catch (Exception ex)
      {
         result.Success = false;
         result.DynamicObject = ex;
         Logger.Instance.Error(result, this);
      }
    }
  }
}

This is the basics of most of the code using the automapper. I have a few repositories, but they are all coupled closely with that darn automapper IMapper object. would prefer to using my Unity.cs class to pass in that object to the container and just have Unity pass in the object to the constructors of my repository classes. Problem is, I can't get it working quite right and can't find the exact documentation to help out here either. If I can't get Unity or Microsoft injection methods to work I guess the next best thing would be a singleton like I am doing with my logger class. Anyone have any suggestions? google isn't being real helpful here because all I can find is old solutions for stuff prior to automapper 5.0 which isn't really helping me.

The previous solution was to create a profile class which overrides the Configure method of Profile.Configure. This problem is that is now obsolete. So I am trying to avoid going down that route if I can help it. This is a link to the example of the way of using Unity previously with the override Configure method on profiles

http://stackoverflow.com/questions/...er-imappingengine-using-unity-in-webapi-proje

For those following along the code and wondering what that Result class is, it is basically this. Allows a cleaner pass back of either the object I want to the caller or an error object if needed.

Result.cs
Code:
public class Result
{
  public bool Success { get; set; }
  public string Message { get; set; }
  public object DynamicObject { get; set; }
}
 
Last edited:

Merad

Platinum Member
May 31, 2010
2,586
19
81
Hm, I didn't even know Unity existed. I've always used Autofac (and highly recommend it). The general pattern with AutoMapper 5 is to create your configuration, and store the result (IConfigurationProvider, IIRC) as a singleton in the IoC container. You then need a registration for IMapper that tells the IoC container to build it by pulling the IConfigurationProvider out of the container and using it to create a Mapper object. If your mappings use value resolvers, type converters, etc. and you would like automapper to resolve them using IoC, the second parameter of Mapper takes an optional func that should call back out to the IoC container and create a requested type.
 

HumblePie

Lifer
Oct 30, 2000
14,665
440
126
Hm, I didn't even know Unity existed. I've always used Autofac (and highly recommend it). The general pattern with AutoMapper 5 is to create your configuration, and store the result (IConfigurationProvider, IIRC) as a singleton in the IoC container. You then need a registration for IMapper that tells the IoC container to build it by pulling the IConfigurationProvider out of the container and using it to create a Mapper object. If your mappings use value resolvers, type converters, etc. and you would like automapper to resolve them using IoC, the second parameter of Mapper takes an optional func that should call back out to the IoC container and create a requested type.

Unity is Microsoft's nice new clean approach to dependency injection or framework injection. Pretty simple and straight forward for the most part. Create a container object of UnityContainer type. Register object instances or object types or interfaces with the container during startup. As long as you do so, it will build the object for you if it is needed to be passed in and not already built. Standard factory and DI setup.

The problem is that AutoMapper has almost always been a static instance object. It was at 4.2 that it was changed, and then with 5.0.2 added back in. I can make it into a singleton or use Unity with it, the problem is that I am calling on code that is considered deprecated or worse at this point. So for now I've gone back to using it as a static class again. Which removes my dependencies to some degree (well not really, but truth be told even DI pattern stuff only really loosens dependencies), but the problem with using it as a static object is that you are hoping the object doesn't have a mem leak issue if it is being used every where and not properly disposed. Normally the clean up actions of the compiler do this well enough for you, but you never know what the makers of a specific library are doing on the backend unless you dig into it yourself.
 

Merad

Platinum Member
May 31, 2010
2,586
19
81
Unity is Microsoft's nice new clean approach to dependency injection or framework injection. Pretty simple and straight forward for the most part. Create a container object of UnityContainer type. Register object instances or object types or interfaces with the container during startup. As long as you do so, it will build the object for you if it is needed to be passed in and not already built. Standard factory and DI setup.

Sounds like your standard DI framework. Some googling shows that MS apparently abandoned it a year ago. I guess they thought it was redundant given the existing selection of OSS DI libraries plus the DI built into .NET Core's MVC.

The problem is that AutoMapper has almost always been a static instance object.

The global instance was never removed, but 4.2 banned you from creating mappings globally. This was because tons of devs (including half the ones where I work, sigh) were being lazy and doing:

Code:
Mapper.CreateMap<Foo, Bar>();
Mapper.Map<Foo, Bar>(...);

However, I'd be extremely shocked if the global instance isn't removed from 6.0.

Anyway, I fail to see why you can't use AutoMapper with your DI, regardless of what library you use? This is the registration process for Autofac, I'm sure you can translate it to Unity:

Code:
var config = new MapperConfiguration(config => /* here you add profiles, call config.CreateMap(), etc */);
// optional
config.AssertConfigurationIsValid();
// builder is Autofac's setup
builder.RegisterInstance(config)
  .As<IConfigurationProvider>();
  .SingleInstance();
// IMapper's are lightweight and created as needed
builder.Register(context =>
{
  var provider = context.Resolve<IConfigurationProvider>();
  var resolver = context.Resolve<IComponentContext>();
  return new Mapper(provider, type => resolver.Resolve(type));
}).As<IMapper>();
 

HumblePie

Lifer
Oct 30, 2000
14,665
440
126
Sounds like your standard DI framework. Some googling shows that MS apparently abandoned it a year ago. I guess they thought it was redundant given the existing selection of OSS DI libraries plus the DI built into .NET Core's MVC.



The global instance was never removed, but 4.2 banned you from creating mappings globally. This was because tons of devs (including half the ones where I work, sigh) were being lazy and doing:

Code:
Mapper.CreateMap<Foo, Bar>();
Mapper.Map<Foo, Bar>(...);

However, I'd be extremely shocked if the global instance isn't removed from 6.0.

Anyway, I fail to see why you can't use AutoMapper with your DI, regardless of what library you use? This is the registration process for Autofac, I'm sure you can translate it to Unity:

Code:
var config = new MapperConfiguration(config => /* here you add profiles, call config.CreateMap(), etc */);
// optional
config.AssertConfigurationIsValid();
// builder is Autofac's setup
builder.RegisterInstance(config)
  .As<IConfigurationProvider>();
  .SingleInstance();
// IMapper's are lightweight and created as needed
builder.Register(context =>
{
  var provider = context.Resolve<IConfigurationProvider>();
  var resolver = context.Resolve<IComponentContext>();
  return new Mapper(provider, type => resolver.Resolve(type));
}).As<IMapper>();

Unity is still being update and hasn't been abandoned. Well Microsoft hasn't updated the official site info on it in awhile, but the actual product is up to 4.0.1 and you can grab it from Nuget. Kind of makes figuring out what was changed a pain in the posterior sometimes.

As far as automapper, the static global was removed temporarily and put back. It was firs set to deprecated, then remove, then added back. Patch notes even say that.

It's not that I can't use DI for automapper, it's that any of the code examples I've used for that are marked deprecated or obsolete. I CAN use the code still that way, but I was trying to find the "new" future proof way the are expecting to go with the code for DI. I haven't been able to find it still. So for now I'm back to using the static object instead.
 

Merad

Platinum Member
May 31, 2010
2,586
19
81
Unity is still being update and hasn't been abandoned. Well Microsoft hasn't updated the official site info on it in awhile, but the actual product is up to 4.0.1 and you can grab it from Nuget. Kind of makes figuring out what was changed a pain in the posterior sometimes.

I found an announcement yesterday where MS announced in 2015 that they were turning it over to the open source community. However, the last release was almost a year ago and the github repo hasn't seen any activity in 6 months.

What *exactly* are you doing that is marked deprecated? I showed you in my last post how AutoMapper should be set up with DI. All that's left is to use constructor or property injection to obtain an IMapper instance, then call .Map() on it. That *is* the "future proof" way.
 

HumblePie

Lifer
Oct 30, 2000
14,665
440
126
I found an announcement yesterday where MS announced in 2015 that they were turning it over to the open source community. However, the last release was almost a year ago and the github repo hasn't seen any activity in 6 months.

What *exactly* are you doing that is marked deprecated? I showed you in my last post how AutoMapper should be set up with DI. All that's left is to use constructor or property injection to obtain an IMapper instance, then call .Map() on it. That *is* the "future proof" way.

The project is using Unity as that was selected by the company and not me. The documentation I had found for making an DI instance of automapper shows overriding the Configure method on the Profile which is marked as obsolete right now.