I'm trying to inject an instance of ISession
into a custom AutoMapper ValueResolver
.
Here's the resolver
public class ContactTypeResolver
: ValueResolver<Common.Models.ContactType, Models.ContactType>
{
ISession _session;
public ContactTypeResolver(ISession session)
{
_session = session;
}
protected override Models.ContactType ResolveCore(Common.Models.ContactType source)
{
return _session.Load<Models.ContactType>(source.Id);
}
}
I have a profile for setting up AutoMapper
this.CreateMap<Models.PhoneNumber, Common.Models.PhoneNumber>()
.ReverseMap()
.ForMember(d => d.Type, o => o.ResolveUsing<ContactTypeResolver>());
I register the resolver in a StructureMap registry like so
For<ValueResolver<Common.Models.ContactType, Models.ContactType>>()
.Add<ContactTypeResolver>();
I am using session-per-request, and I am set the session inside of a nested container inside of StructureMapDependencyScope.cs
which was created when I added StructureMap to my Web Api project. Here's the code
public void CreateNestedContainer()
{
if (CurrentNestedContainer != null)
{
return;
}
CurrentNestedContainer = Container.GetNestedContainer();
CurrentNestedContainer.Configure(c => c.For<ISession>().Use(ctx => ctx.GetInstance<ISessionFactory>().OpenSession()));
}
And this is how my container is set
var container = StructuremapMvc.StructureMapDependencyScope.Container;
GlobalConfiguration.Configuration.DependencyResolver = new StructureMapWebApiDependencyResolver(container);
container.Configure(x =>
{
x.IncludeRegistry<DefaultRegistry>();
});
Mapper.Initialize(cfg =>
{
cfg.ConstructServicesUsing(container.GetInstance);
foreach (var profile in container.GetAllInstances<Profile>())
cfg.AddProfile(profile);
});
I even tried using the service locator like so
this.CreateMap<Models.PhoneNumber, Common.Models.PhoneNumber>()
.ReverseMap()
.ForMember(d => d.Type, o => o
.ResolveUsing<ContactTypeResolver>()
.ConstructedBy(StructureMap.ObjectFactory.GetInstance<ContactTypeResolver>));
However, when the code runs I get a run-time exception stating
No default Instance is registered and cannot be automatically determined for type 'NHibernate.ISession'.
I also tried injecting another type registered in my container, which also did not work. What am I missing? The session instance does get injected into other objects. Also, other mappings that don't require dependency injection in the same profile work just fine.
I believe @RadimKöhler is right.
Your parent container (where the AutoMapper types are configured) doesn't have access to the plugin types defined in the nested container. It just doesn't know about the
ISession
plugin type hence the exception you get.I can think of a couple solutions here. DISCLAIMER I didn't tested them.
ISession
type to your parent container. You should be able to scope it to HTTP requests there as well.For example with the following registration code. I have no knowledge about ASP.NET WebAPI so there might be some differences but you get the point.
In general, I would say, that the issue with:
we should solve by registering
ISession
inside of our container:What would be the use? It could be our own way how to retrieve the contextual
ISession
. There could be code like this:This will be enough for StructureMap to properly inject this kind of instance into our
Web API
Service.Behind
MySessionProvider.GetCurrentSession()
could be some call to static internal API which returns ISession related to HttpContext (initiated and disposed at request Start and End)Or we can follow this:
Check this part of doc/guidance as well:
Retrieving a Service from IContext
and the subsection:
Retrieving a Service from IContext, which shows that we can do it like this