我正在尝试将ISession
的实例注入自定义 AutoMapper ValueResolver
。
这是解析器
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);
}
}
我有设置AutoMapper的个人资料
this.CreateMap<Models.PhoneNumber, Common.Models.PhoneNumber>()
.ReverseMap()
.ForMember(d => d.Type, o => o.ResolveUsing<ContactTypeResolver>());
我在一个StructureMap注册表中注册了解析器,如此
For<ValueResolver<Common.Models.ContactType, Models.ContactType>>()
.Add<ContactTypeResolver>();
我正在使用session-per-request,我将会话设置在StructureMapDependencyScope.cs
内的嵌套容器内,这是在我将StructureMap添加到Web Api项目时创建的。这是代码
public void CreateNestedContainer()
{
if (CurrentNestedContainer != null)
{
return;
}
CurrentNestedContainer = Container.GetNestedContainer();
CurrentNestedContainer.Configure(c => c.For<ISession>().Use(ctx => ctx.GetInstance<ISessionFactory>().OpenSession()));
}
这就是我的容器设置方式
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);
});
我甚至尝试使用像这样的服务定位器
this.CreateMap<Models.PhoneNumber, Common.Models.PhoneNumber>()
.ReverseMap()
.ForMember(d => d.Type, o => o
.ResolveUsing<ContactTypeResolver>()
.ConstructedBy(StructureMap.ObjectFactory.GetInstance<ContactTypeResolver>));
然而,当代码运行时,我得到一个运行时异常陈述
没有注册默认实例,无法自动确定类型'NHibernate.ISession'。
我也试过注入我的容器中注册的另一种类型,这也没用。我错过了什么?会话实例会注入其他对象。此外,在同一个配置文件中不需要依赖注入的其他映射也可以正常工作。
答案 0 :(得分:2)
我相信@RadimKöhler是对的。
您的父容器(配置了AutoMapper类型)无法访问嵌套容器中定义的plugin types。它只是不知道ISession
plugin type因此得到的例外。
我可以在这里想到几个解决方案。 免责声明我没有对它们进行过测试。
ISession
类型的注册移动到您的父容器。您也应该能够将其范围限定为HTTP请求。例如,使用以下注册码。我对ASP.NET WebAPI一无所知,所以可能存在一些差异,但你明白了。
public class NHibernateRegistry : Registry
{
public NHibernateRegistry()
{
For<Configuration>().Singleton().Use(c => new ConfigurationFactory().AssembleConfiguration());
For<ISessionFactory>().Singleton().Use(c => c.GetInstance<Configuration>().BuildSessionFactory());
For<ISession>().HybridHttpOrThreadLocalScoped().Use(c =>
{
var sessionFactory = c.GetInstance<ISessionFactory>();
return sessionFactory.OpenSession();
});
}
}
答案 1 :(得分:1)
一般来说,我会说,问题是:
没有注册默认实例... ISession
我们应该通过在容器内注册ISession
来解决:
x.For<ISession>()
.Use...
有什么用?如何检索上下文ISession
可能是我们自己的方式。可能有这样的代码:
x.For<ISession>()
.Use(() => MySessionProvider.GetCurrentSession());
这足以让 StructureMap 将这种实例正确地注入我们的Web API
服务中。
在MySessionProvider.GetCurrentSession()
后面可以调用静态内部API,它返回与HttpContext 相关的 ISession (在请求开始和结束时启动和处理)
或者我们可以遵循:
同时检查这部分文档/指南:
和小节:
从IContext检索服务,表明我们可以这样做
您还可以在对象构造期间从IContext检索其他服务。因为底层BuildSession管理自动布线,所以通常可以假设您正在使用与同一对象图中的其他对象将接收的PluginType完全相同的对象实例。当您谈论在任何类型的桌面应用程序或任何类型的NHibernate对象中使用View时,这是一个有用的功能,其中所请求对象的状态或身份很重要。
我的团队在NHibernate引导中使用此功能。我们有一个名为ISessionSource的接口,它负责创建NHibernate ISession对象(它包装了一个Session)。
public interface ISessionSource
{
ISession CreateSession();
}
我们不能直接走路并直接创建一个ISession对象。相反,您必须使用ISessionSource为您创建ISession。我们仍然希望StructureMap将ISession对象注入到其他类中,因此我们使用Lambda中的
IContext.GetService<ISession>()
方法来构建ISession对象:
ForRequestedType<ISession>().TheDefault.Is.ConstructedBy(
context => context.GetInstance<ISessionSource>().CreateSession());