20
\$\begingroup\$

I'd like to know if I'm doing profile configuration in the wrong place or in the wrong way.

I'm following the Onion Architecture, so that restricts the direction of my dependencies towards the center.

Onion Architecture

Core

My domain model and AutoMapper facade:

namespace Core.Domain
{
    public class MyModel
    {
        // model stuff
    }
}

namespace Core.Services
{
    public interface IMapper
    {
        object Map(object source, Type sourceType, Type destinationType);
    }
}

Infrastructure

AutoMapper facade implementation:

namespace Infrastructure.Mapping
{
    public class Mapper : IMapper
    {
        private readonly IMappingEngine _mappingEngine;

        public Mapper(IMappingEngine mappingEngine)
        {
            _mappingEngine = mappingEngine;
        }

        public object Map(object source, Type sourceType, Type destinationType)
        {
            return _mappingEngine.Map(source, sourceType, destinationType);
        }
    }
}

UI

This is my controller and view model. I'm using the AutoMapper via a filter, following this example.

namespace UI.Controllers
{
    public class HomeController : Controller
    {    
        [AutoMap(typeof(MyModel), typeof(MyViewModel))]
        public ActionResult Index()
        {
            var myItem = _myRepository.GetById(0);
            return View(myItem);
        }
    }
}

namespace UI.ViewModels
{
    public class MyViewModel
    {
        // view stuff
    }
}

Dependency Resolution

This is where I have my doubts:

namespace DependencyResolution
{
    public class MappingModule : NinjectModule
    {
        public override void Load()
        {
            Mapper.Initialize(cfg => cfg.AddProfile(new MyProfile()));
            Bind<IMappingEngine>().ToMethod(ctx => Mapper.Engine);
            Bind<IMapper>().To<Mapping.Mapper>();

            Kernel.BindFilter<AutoMapFilter>(FilterScope.Controller, 0)
                  .WhenActionMethodHas<AutoMapAttribute>()
                  .WithConstructorArgumentFromActionAttribute<AutoMapAttribute>("sourceType", att => att.SourceType)
                  .WithConstructorArgumentFromActionAttribute<AutoMapAttribute>("destType", att => att.DestType);
        }
    }

    public class MyProfile : Profile
    {
        protected override void Configure()
        {
            Mapper.CreateMap<MyModel, MyViewModel>().ForMember(...);
        }
    }
}

Questions

  1. Is the way I bind to AutoMapper wrong?
  2. Is this the wrong place for the profile (keep in mind the dependency restriction)?

In the ideal world I would have placed

Mapper.CreateMap<MyModel, MyViewModel>().ForMember(...)

in Global.asax, but how do I expose CreateMap without referencing AutoMapper?

Is there anything else you have noticed?

\$\endgroup\$
5
  • \$\begingroup\$ I assume that this code works the way you intended it to work. and it sounds like you want to create a session variable to hold your Mapper, is that what you are asking there about the Mapper.CreateMap<MyModel, MyViewModel>().ForMember(...)??? \$\endgroup\$ – Malachi Oct 29 '13 at 19:22
  • \$\begingroup\$ How did you solve this eventually? \$\endgroup\$ – Groo Dec 2 '13 at 13:18
  • \$\begingroup\$ Its been a while, but let me try to recap. I ended up using the above. However I had to use post build scripts to move assemblies to the MVC project which wasn't too bad. But the MVC publish functionallity doesn't run custom scripts, so that was a major pain. I had to deploy manually. So these days I just put AutoMapper and Ninject inside the MVC project, they're still easy to switch out if needed. \$\endgroup\$ – Snæbjørn Mar 20 '14 at 21:30
  • \$\begingroup\$ Yes - this is the correct way to do this...don't take my word for it though, read what the architect of Onion Architecture had to say on a very similar question... Jeffrey Palermo reply \$\endgroup\$ – user921773 Jul 4 '14 at 12:26
  • \$\begingroup\$ @Snæbjørn Just to comment. From what I've read a purist way would be to have a Bootstrapper layer which does the wiring for you and hooks into MVC startup modules. Hence the WebApp would only reference the Core which would contain your interfaces.. \$\endgroup\$ – dreza Aug 5 '14 at 8:39
5
\$\begingroup\$

What is the purpose of the IMapper interface and Mapper class? It looks to me that they are just wrapping the IMappingEngine interface and MappingEngine class. While this is a good method when you have a third party class that doesn't have an interface, I think it is overkill here. Why don't you just use the IMappingEngine where you need that functionality?

If you are going to keep your Mapper class, I would rename it, having two Mapper classes is confusing.

As for where it is, I don't have a problem with doing it this way. All the wire-up is done in one place, and its easy to find and add to as needed.

\$\endgroup\$
2
  • 5
    \$\begingroup\$ I did it because I'm not aware of a way to extract the IMappingEngine from the assembly. So I would have to reference AutoMapper which is what I wanted to avoid. So IMapper should be a generic mapping interface which any mapping library could build an adapter for. \$\endgroup\$ – Snæbjørn Mar 20 '14 at 21:35
  • \$\begingroup\$ I think this is over eagerly trying to avoid dependencies in code. Certain aspects (mapping, logging, ...) should not require a custom wrapper library. They provide sufficient layers and maturity to introspect and extend them. \$\endgroup\$ – dfhwze Jul 22 '19 at 21:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.