将 ISession 注入自定义值解析器

Inject ISession into custom valueresolver

我正在尝试将 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>();

我正在使用每个请求的会话,并且我在 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>));

然而,当代码 运行s 我得到一个 运行 时间异常说明

No default Instance is registered and cannot be automatically determined for type 'NHibernate.ISession'.

我还尝试注入在我的容器中注册的另一种类型,但也没有用。我错过了什么?会话实例确实被注入到其他对象中。此外,在同一配置文件中不需要依赖项注入的其他映射也可以正常工作。

一般来说,我会说,问题在于:

No default Instance is registered ... ISession

我们应该通过在我们的容器中注册 ISession 来解决:

x.For<ISession>()
 .Use...

有什么用?如何检索上下文 ISession 可能是我们自己的方式。可能有这样的代码:

x.For<ISession>()
 .Use(() => MySessionProvider.GetCurrentSession());

这足以让 StructureMap 将这种实例正确地注入到我们的 Web API 服务中。

MySessionProvider.GetCurrentSession() 后面可能是对静态内部 API 的一些调用,其中 returns ISession 与 HttpContext (已启动并按要求处理 Start 和 End)

或者我们可以按照这个:

  • NHibernate and Structure Map
  • Setting up Fluent NHibernate and StructureMap for a web application

同时检查 doc/guidance 的这一部分:

Retrieving a Service from IContext

和小节:

Retrieving a Service from IContext,这表明我们可以这样做

You can also retrieve other services from the IContext during object construction. Because the underlying BuildSession manages the Auto Wiring, you can generally assume that you're using the exact same object instance for a PluginType that other objects in the same object graph will receive. That's a helpful feature when you're talking about using View's within any type of desktop application or any kind of NHibernate object where the state or identity of the object requested is important.

My team uses this functionality in our NHibernate bootstrapping. We have an interface named ISessionSource that is responsible for creating the NHibernate ISession objects (it wraps a Session).

public interface ISessionSource
{
    ISession CreateSession();
}

We can't just walk up and create an ISession object directly. Instead, you have to use the ISessionSource to create an ISession for you. We still want StructureMap to inject the ISession objects into other classes, so we use the IContext.GetService<ISession>() method from within a Lambda to build ISession objects:

ForRequestedType<ISession>().TheDefault.Is.ConstructedBy(
    context => context.GetInstance<ISessionSource>().CreateSession());

我相信@RadimKöhler 是对的。

您的父容器(配置 AutoMapper 类型的地方)无权访问 plugin types defined in the nested container. It just doesn't know about the ISession plugin type 因此出现异常。

我可以在这里想到几个解决方案。 免责声明我没有测试它们。

  1. 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. Tell AutoMapper you want to use the nested container.