【问题标题】:Inject ISession into custom valueresolver将 ISession 注入自定义 valueresolver
【发布时间】:2015-05-06 15:44:36
【问题描述】:

我正在尝试将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”。

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

【问题讨论】:

    标签: nhibernate asp.net-web-api dependency-injection automapper structuremap


    【解决方案1】:

    我相信@RadimKöhler 是对的。

    您的父容器(配置 AutoMapper 类型的位置)无权访问嵌套容器中定义的 plugin types。它只是不知道 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

    【讨论】:

    • 出于某种原因,我认为不应使用 structuremap.web 和 HybridHttpOrThreadLocalScoped。无论如何,它工作得很好......
    • 顺便说一句,我的StructureMapDependencyScope.cs 中需要CurrentNestedContainer.Configure(c =&gt; c.For&lt;ISession&gt;().Use(ctx =&gt; ctx.GetInstance&lt;ISessionFactory&gt;().OpenSession())); 吗?
    • @JoshC。在父容器中注册应该足够了。如果没有其他注册,您甚至可以摆脱嵌套容器。
    【解决方案2】:

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

    没有注册默认实例...ISession

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

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

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

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

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

    MySessionProvider.GetCurrentSession() 后面可能是对静态内部 API 的调用,该 API 返回与 HttpContext 相关的 ISession(在请求开始和结束时启动和处理)

    或者我们可以按照这个:

    也检查这部分文档/指南:

    Retrieving a Service from IContext

    和小节:

    Retrieve a Service from IContext,说明我们可以这样做

    您还可以在对象构造期间从 IContext 检索其他服务。因为底层的 BuildSession 管理着 Auto Wiring,所以你通常可以假设你使用的 PluginType 的对象实例与同一对象图中的其他对象将接收到的完全相同。当您谈论在任何类型的桌面应用程序或任何类型的 NHibernate 对象中使用 View 时,这是一个有用的功能,其中请求的对象的状态或身份很重要。

    我的团队在我们的 NHibernate 引导中使用了这个功能。我们有一个名为 ISessionSource 的接口,它负责创建 NHibernate ISession 对象(它包装了一个 Session)。

    public interface ISessionSource
    {
        ISession CreateSession();
    }
    

    我们不能直接走上去创建一个 ISession 对象。相反,您必须使用 ISessionSource 为您创建 ISession。我们仍然希望 StructureMap 将 ISession 对象注入到其他类中,因此我们使用 Lambda 中的 IContext.GetService&lt;ISession&gt;() 方法来构建 ISession 对象:

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

    【讨论】:

    • 感谢您的回复。我可能已经省略了如何为每个请求创建会话。我已经相应地更新了我的问题。基本上,我创建了一个生命周期与 http 请求匹配的嵌套容器。
    • 这里的重点是,嵌套容器有 ISession 的定义,但是创建 Resolver 的外部/父级没有这样的注册。 注意我使用的是 StructureMap 3 - 没有这些嵌套容器... 根据我阅读的内容,重点是您应该使用 context.GetInstance.. 而不是 StructureMap.ObjectFactory.. 检查此@987654324 @
    • 我明白,但我仍然不太确定该怎么做。除了我的 AutoMapper 自定义 ValueResolver 之外,我的嵌套容器中的 ISession 到处都被注入,而且我不知道如何在我的 AM 配置文件中访问 lambda 中的上下文。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-02
    • 2017-07-27
    • 1970-01-01
    • 1970-01-01
    • 2021-08-21
    相关资源
    最近更新 更多