Where to place AutoMapper map registration in referenced dll
Asked Answered
C

2

2

This is my first AutoMapper project and may be obvious to some but the tutorials and examples are not clicking with me. I am trying to understand where and to a certain degree how to register(I think I want profiles) my maps for use. There are plenty of MVC examples saying to use the global asax and this makes sense but what is the equivalent in a library project?

In my sandbox I have a winform app and a core library. The winform app calls methods made available by the library and it is one of these library methods that makes use of automapper.

So for some background here is my map: (and to be clear the mapping is in the SAME core library project)

public class Raw_Full_Map    
{
    public Raw_Full_Map()
    {

        Mapper.CreateMap<IEnumerable<RawData>, FullData>()
            .ForMember(d => d.Acres, m => m.ResolveUsing(new RawLeadDataNameResolver("Acres")));
       //this is clearly just a snip to show it's a basic map
    }
 }

This is the core library method being called: (note it is a static..which means I won't have a constructor...if this is the problem am I to understand then that AutoMapper can't be utilized by static helper classes...that doesn't make sense....so likely I'm just not doing it right.

public static class RawDataProcessing
{

    public static FullData HTMLDataScrape(string htmlScrape)
    {

        HtmlDocument doc = new HtmlDocument();
        doc.LoadHtml(htmlScrape);

        var list = Recurse(doc.DocumentNode);
        //HTML agility stuff that turns my html doc into a List<RawData> object


        return Mapper.Map<FullData>(list);


    }

My test harness calls it like this:

var _data = RawDataProcessing.HTMLDataScrape(rawHTML);

This of course errors because the map isn't "registered".

If I do this in the test harness:

var x = new RawData_FullData();

var _data = RawDataProcessing.HTMLDataScrape(rawHTML);

Then everything works as my map get's registered albeit I think in a really bogus way...but it does work.

So the question is how do I register my mapping in the core library project...so that ANY method can use it...there isn't really an equivalent global.asax in a dll is there?

Thank you for helping me connect the missing pieces.

Chevrette answered 9/5, 2015 at 5:33 Comment(0)
T
4

Put it in the static constructor of either the source or the target type of the mapping.

public class FullData
{
    static FullData()
    {

        Mapper.CreateMap<IEnumerable<RawData>, FullData>()
            .ForMember(d => d.Acres, m => m.ResolveUsing(new RawLeadDataNameResolver("Acres")));
    }
 }

The static constructor will automatically get called the first time you try to use the type FullData for anything (for example a mapping).

Thy answered 9/5, 2015 at 6:7 Comment(1)
AHHHH. I didn't know you could do a static constructor...just use the keyword static...seems so straight forward. Thank You!!Chevrette
S
1

You can use PreApplicationStartMethod for any class and it's method in your class library which will be referenced from your startup project if you want automatically to call this on startup. And then you can register all your mappings in that method. By the way, I suggest to use AddProfile for registering all mappings.

[assembly: PreApplicationStartMethod(typeof(MyClassLibrary.Startup), "Start")]
namespace MyClassLibrary
{
    public class Startup
    {
        // Automatically will work on startup
        public static void Start()
        {
              Mapper.Initialize(cfg =>
              {
                    Assembly.GetExecutingAssembly().FindAllDerivedTypes<Profile>().ForEach(match =>
                    {
                        cfg.AddProfile(Activator.CreateInstance(match) as Profile);
                    });
              });
        }
    }
}

You just need to create new classes which derived from Profile class and then override it's Configure() method:

...
public class FooMapperProfile:Profile
{
    protected override void Configure()
    {
        Mapper.CreateMap<OtherFoo, Foo>()
              .ForMember(...
              ... // so on
    }
}

public class AnotherFooMapperProfile:Profile
{
    protected override void Configure()
    {
        Mapper.CreateMap<OtherFoo, AnotherFoo>()
              .ForMember(...
              ... // so on;
    }
}
... 
// and so on

Additional information: If you have seen I have initialized all mappings with that code:

Mapper.Initialize(cfg =>
{
        Assembly.GetExecutingAssembly().FindAllDerivedTypes<Profile>().ForEach(match =>
        {
                cfg.AddProfile(Activator.CreateInstance(match) as Profile);
        });
});

It will automatically find all types derived from Profile and will add all profiles after createing their new instances.

Update1:

As @Scott Chamberlain commented, PreApplicationStartMethod only works for ASP.NET applications. This would not work with a desktop app. If you are working with Wpf, then you can use Application.OnStartup method. Or just call Start.Startup (); in load event.

Update2:
FindAllDerivedTypes extension method:

 public static class AssemblyExtensions
    {
        public static List<Type> FindAllDerivedTypes<T>(this Assembly assembly)
        {
            var derivedType = typeof(T);
            return assembly.GetTypes()
                          .Where(t => t != derivedType && derivedType.IsAssignableFrom(t))
                          .ToList();

        }
    }
Sick answered 9/5, 2015 at 7:18 Comment(8)
Yes I knew to use profiles but you provided the new information about PreApplicationStartMethod and something I hadn't seen in the automapper profile tutorials was the slick linq lambda to register everything in one shot. Very Cool. Thank You.Chevrette
Are there multiple namespaces for PreApplicationStartMethod? I see it in Webactivator, and system.web..do I just add one of those to my class library to get the "effect" even though it's not a web project?Chevrette
@user1278561 Yes, As I know you can use System.Web.Sick
One important note, PreApplicationStartMethod only works for ASP.NET applications. This would not work with a desktop app.Thy
I am also struggling with FindAllDerivedTypes<T>. From what I can tell it is an extension..but is it existing in some Msoft namespace (closest I found was Msoft.Data.Edm) or one I write...found several examples googling but is that what was intended?Chevrette
@user1278561 Yes, sorry. I have included this extension method method to my answer.Sick
@user1278561 If you are not referencing this library from Asp.net applicaiotn, then you can use Application_startup method as I suggested in my Update1Sick
Will PreApplicationStartMethod going to work for class library project ?Acatalectic

© 2022 - 2024 — McMap. All rights reserved.