I have been using AutoMapper for some time now. I have a profile setup like so:
public class ViewModelAutoMapperConfiguration : Profile
{
protected override string ProfileName
{
get { return "ViewModel"; }
}
protected override void Configure()
{
AddFormatter<HtmlEncoderFormatter>();
CreateMap<IUser, UserViewModel>();
}
}
I add this to the mapper using the following call:
Mapper.Initialize(x => x.AddProfile<ViewModelAutoMapperConfiguration>());
However, I now want to pass a dependency into the ViewModelAutoMapperConfiguration constructor using IoC. I am using Autofac. I have been reading through the article here: http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/05/11/automapper-and-ioc.aspx but I can't see how this would work with Profiles.
Any ideas?
Thanks
Well, I found a way of doing it by using an overload of AddProfile. There is an overload that takes an instance of a profile, so I can resolve the instance before passing it into the AddProfile method.
A customer of mine was wondering the same thing as DownChapel and his answer triggered me in writing some sample application.
What I've done is the following.
First retrieve all Profile types from the asseblies and register them in the IoC container (I'm using Autofac).
var loadedProfiles = RetrieveProfiles();
containerBuilder.RegisterTypes(loadedProfiles.ToArray());
While registering the AutoMapper configuration I'm resolving all of the Profile types and resolve an instance from them.
private static void RegisterAutoMapper(IContainer container, IEnumerable<Type> loadedProfiles)
{
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.ConstructServicesUsing(container.Resolve);
foreach (var profile in loadedProfiles)
{
var resolvedProfile = container.Resolve(profile) as Profile;
cfg.AddProfile(resolvedProfile);
}
});
}
This way your IoC-framework (Autofac) will resolve all dependencies of the Profile, so it can have dependencies.
public class MyProfile : Profile
{
public MyProfile(IConvertor convertor)
{
CreateMap<Model, ViewModel>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Identifier))
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => convertor.Execute(src.SomeText)))
;
}
}
The complete sample application can be found on GitHub, but most of the important code is shared over here.
Related
I am trying to inject the IApplicationConfigurationSection implementation into this MVC5 Controller, so that I can have access to some of the information (various strings) from my web.config custom section in all of my views:
public class BaseController : Controller
{
public IApplicationConfigurationSection AppConfig { get; set; }
public BaseController()
{
ViewBag.AppConfig = AppConfig; // AppConfig is always null
}
}
I want to use setter injection so I don't have to clutter up my derived Controller constructors with parameters that they don't really care about.
Note: If there is a better way to inject base class dependencies, please let me know. I admit I may not be on the right track here.
In my Global.asax I load my StructureMap configurations:
private static IContainer _container;
protected void Application_Start()
{
_container = new Container();
StructureMapConfig.Configure(_container, () => Container ?? _container);
// redacted other registrations
}
My StructureMapConfig class loads my registries:
public class StructureMapConfig
{
public static void Configure(IContainer container, Func<IContainer> func)
{
DependencyResolver.SetResolver(new StructureMapDependencyResolver(func));
container.Configure(cfg =>
{
cfg.AddRegistries(new Registry[]
{
new MvcRegistry(),
// other registries redacted
});
});
}
}
My MvcRegistry provides the mapping for StructureMap:
public class MvcRegistry : Registry
{
public MvcRegistry()
{
For<BundleCollection>().Use(BundleTable.Bundles);
For<RouteCollection>().Use(RouteTable.Routes);
For<IPrincipal>().Use(() => HttpContext.Current.User);
For<IIdentity>().Use(() => HttpContext.Current.User.Identity);
For<ICurrentUser>().Use<CurrentUser>();
For<HttpSessionStateBase>()
.Use(() => new HttpSessionStateWrapper(HttpContext.Current.Session));
For<HttpContextBase>()
.Use(() => new HttpContextWrapper(HttpContext.Current));
For<HttpServerUtilityBase>()
.Use(() => new HttpServerUtilityWrapper(HttpContext.Current.Server));
For<IApplicationConfigurationSection>()
.Use(GetConfig());
Policies.SetAllProperties(p => p.OfType<IApplicationConfigurationSection>());
}
private IApplicationConfigurationSection GetConfig()
{
var config = ConfigurationManager.GetSection("application") as ApplicationConfigurationSection;
return config; // this always returns a valid instance
}
}
I have also "thrown my hands up" and tried using the [SetterProperty] attribute on the BaseController - that technique failed as well.
Despite my best efforts to find a solution, the AppConfig property in my controller's constructor is always null. I thought that
`Policies.SetAllProperties(p => p.OfType<IApplicationConfigurationSection>());`
would do the trick, but it didn't.
I have found that if I discard setter injection and go with constructor injection, it works as advertised. I'd still like to know where I'm going wrong, but I'd like to stress that I'm not a StructureMap guru - there may be a better way to avoid having to constructor-inject my base class dependencies. If you know how I should be doing this but am not, please share.
While constructor injection in this scenario appears to be the better solution to the stated problem as it follows The Explicit Dependencies Principle
Methods and classes should explicitly require (typically through method parameters or constructor parameters) any collaborating objects they need in order to function correctly.
The mention of only needing to access the AppConfig in your views leads me to think that this is more of an XY problem and a cross cutting concern.
It appears that the controllers themselves have no need to use the dependency so stands to reason that there is no need to be injecting them into the controller explicitly just so that the dependency is available to the View.
Consider using an action filter that can resolve the dependency and make it available to the View via the same ViewBag as the request goes through the pipeline.
public class AccessesAppConfigAttribute : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext filterContext) {
var resolver = DependencyResolver.Current;
var appConfig = (IApplicationConfigurationSection)resolver.GetService(typeof(IApplicationConfigurationSection));
filterContext.Controller.ViewBag.AppConfig = appConfig;
}
}
This now makes the required information available to the views with out tight coupling of the controllers that may have a use for it. Removing the need to inject the dependency into derived classes.
Either via adorning Controller/Action with the filter attribute
[AccessesAppConfig] //available to all its actions
public class HomeController : Controller {
//[AccessesAppConfig] //Use directly if want to isolate to single action/view
public ActionResult Index() {
//...
return View();
}
}
or globally for all requests.
public class FilterConfig {
public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
filters.Add(new AccessesAppConfigAttribute());
}
}
At this point it really does not matter which IoC container is used. Once the dependency resolver has been configured, Views should have access to the required information in the ViewBag
Previously when I used Automapper v3.x ignoring unmapped properties could be done by simply adding a .IgnoreUnmappedProperties() extension which looked like this
public static class AutoMapperExtensions
{
public static IMappingExpression<TSource, TDestination> IgnoreUnmappedProperties<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
var typeMap = Mapper.FindTypeMapFor<TSource, TDestination>();
if (typeMap != null)
{
foreach (var unmappedPropertyName in typeMap.GetUnmappedPropertyNames())
{
expression.ForMember(unmappedPropertyName, opt => opt.Ignore());
}
}
return expression;
}
}
How can this extension be rewritten to work with Version 5.x. I can of course add the following to each property.
.ForMember(dest => dest.LastUpdatedBy, opt => opt.Ignore())
or not call
Mapper.AssertConfigurationIsValid();
You can do that using the CreateMap method's memberList parameter to specify the validation that you want.
CreateMap<TSource, TDestination>(MemberList.None)
The MemberList.None should do the trick. You can also switch between the source or destination validations.
Automapper - Selecting members to validate
I'm using automapper library to convert my Model into my ViewModel. For each Model, I create profile which inside i add my maps using CreateMap.
I want to use custom ValueResolver in which it will get the logged user ID from IContext, so i need to pass implementation of IContext using Ninject.
Inside my profile class:
Mapper.CreateMap<ViewModel, BusinessModel>()
.ForMember(dest => dest.ManagerId, opt => opt.ResolveUsing<GetManagerResolver>());
Then my GetManagerResolver:
public class GetManagerResolver : ValueResolver<BusinessModel, int>
{
private IContext context;
public GetManagerResolver(IContext context)
{
this.context = context;
}
protected override int GetManagerResolver(BusinessModel source)
{
return context.UserId;
}
}
But i get this exception message {"Type needs to have a constructor with 0 args or only optional args\r\nParameter name: type"}.
Any Ideas on how make automapper use ninject for object creation?
UPDATE
My code to add automapper configuration:
public static class AutoMapperWebConfiguration
{
public static void Configure()
{
Mapper.Initialize(cfg =>
{
cfg.AddProfile(new Profile1());
cfg.AddProfile(new Profile2());
// now i want to add this line, but how to get access to kernel in static class?
// cfg.ConstructServicesUsing(t => Kernel.Get(t));
});
}
}
You can use the ConstructedBy function to configure how Automapper should create your GetManagerResolver after calling ResolveUsing:
Mapper.CreateMap<ViewModel, BusinessModel>()
.ForMember(dest => dest.ManagerId,
opt => opt.ResolveUsing<GetManagerResolver>()
.ConstructedBy(() => kernel.Get<GetManagerResolver>());
Or you can globally sepecify your Ninject kernel to be used by Automapper when resolving any type with Mapper.Configuration.ConstructServicesUsing method:
Mapper.Configuration.ConstructServicesUsing((type) => kernel.Get(type));
What I ended up doing was to create NinjectModule for Automapper where I put all my automapper configuration and tell automapper to use Ninject Kernel to construct objects. Here is my code:
public class AutoMapperModule : NinjectModule
{
public override void Load()
{
Mapper.Initialize(cfg =>
{
cfg.ConstructServicesUsing(t => Kernel.Get(t));
cfg.AddProfile(new Profile1());
cfg.AddProfile(new Profile2());
});
}
}
I have the following NinjectModule derived class:
class MainModule : NinjectModule
{
public override void Load()
{
Bind<IMyClass>().To<MyClass>();
Bind<IMainClass>().To<MainClass>().OnActivation((context,myClass) =>
{ myClass.Add("Something", context.Kernel.Get<IMyClass>()); });
}
}
My problem is that IKernel does not exposed the .Get<T> extension method.
Is there a pattern for doing this?
Caveats: I don't want to have to decorate my classes with Ninject attributes, as the Add is specific to how MainClass works I wanted all the code to do with its creation to be held in this module.
TIA
Hmmm, silly oversight in the end it seems. The extension methods reside in the Ninject namespace, using a module only requires that you are using Ninject.Modules;
Adding using Ninject; meant that the following was possible:
Bind<IMainClass>().To<MainClass>()
.OnActivation((context,myClass) =>
{
foreach(var n in context.Kernel.GetAll<IMyClass>())
{
myClass.Add("Something", n);
}
});
I'll leave this open for a bit to see if anyone has a better way of doing this.
I have an mvc controller which has a helper class injected into it. I would like to convert from a viewmodel to a dto using automapper. most of the properties are simple mappings but one involves calling the helper class with a parameter from the viewmodel. Ideally I would want to do something like this:
Mapper.CreateMap<TheViewModel, TheDto>()
.ForMember(dest => dest.Url, o => o.MapFrom(src => _urlHelper.GenerateUrlFromUsername(src.Username)));
...but I cannot because I cannot access a non-static field.
What is the best approach?
EDIT:
OK, so I have a custom resolver but how do I hook this in to my IoC container?
public class CustomResolver : ValueResolver<TheViewModel, string>
{
private readonly IUrlHelper _urlHelper;
public CustomResolver(IUrlHelper urlHelper)
{
_urlHelper = urlHelper;
}
protected override string ResolveCore(TheViewModel source)
{
return _urlHelper.GenerateUrlFromUsername(source.Username);
}
}
Use a custom resolver in this case:
http://automapper.codeplex.com/wikipage?title=Custom%20Value%20Resolvers
Custom resolvers can be instantiated from a container, so you can get whatever instance fields of services you need.