0

我在 .NET Core 2 上创建了一个基于 NancyFx 的小型 API。它使用 AutoFac 作为 IOC 容器和 NHibernate 5.3 来访问数据库。

我遇到了线程和CurrentSessionContext. 基本上当我进入AfterRequest管道时,我通常在另一个线程上,然后CurrentSessionContext不知道我在请求开始时所做的绑定。

我尝试WebSessionContext改用 ,但由于我使用的是堆栈,所以没有HttpContext.Current. 要访问,HttpContext您必须在需要的地方注入Microsoft.AspNetCore.Http.IHttpContextAccessor

我怎样才能告诉 NHibernate 以某种方式绑定到我自己的上下文,所以我的会话不会在BeforeRequestand之间丢失AfterRequest

为了便于在事务中包装我的数据访问,我在我的 Nancy Bootstrapper 中添加了以下内容:

    protected override void ApplicationStartup(ILifetimeScope container, IPipelines pipelines)
    {
        base.ApplicationStartup(container, pipelines);
        ConfigureNHibernateSessionPerRequest(container, pipelines);
    }

    private void ConfigureNHibernateSessionPerRequest(ILifetimeScope container, IPipelines pipelines)
    {
        pipelines.BeforeRequest += ctx => CreateSession(container);
        pipelines.AfterRequest += ctx => CommitSession(container);
        pipelines.OnError += (ctx, ex) => RollbackSession(container);
    }

    private Response CreateSession(ILifetimeScope container)
    {
        var provider = container.Resolve<INHibernaterSessionFactoryProvider>();
        var sessionFactory = provider.Factory;

        var requestSession = sessionFactory.OpenSession();
        CurrentSessionContext.Bind(requestSession);
        requestSession.BeginTransaction();

        return null;
    }

    private AfterPipeline CommitSession(ILifetimeScope container)
    {
        var provider = container.Resolve<INHibernaterSessionFactoryProvider>();
        var sessionFactory = provider.Factory;

        if (CurrentSessionContext.HasBind(sessionFactory))
        {
            var requestSession = sessionFactory.GetCurrentSession();
            requestSession.Transaction.Commit();
            CurrentSessionContext.Unbind(sessionFactory);
            requestSession.Dispose();
        }
        return null;
    }

    private Response RollbackSession(ILifetimeScope container)
    {
        var provider = container.Resolve<INHibernaterSessionFactoryProvider>();
        var sessionFactory = provider.Factory;

        if (CurrentSessionContext.HasBind(sessionFactory))
        {
            var requestSession = sessionFactory.GetCurrentSession();
            requestSession.Transaction.Rollback();
            CurrentSessionContext.Unbind(sessionFactory);
            requestSession.Dispose();
        }
        return null;
    }

我的 hibernate.cfg.xml 看起来像这样:

    <?xml version="1.0" encoding="utf-8" ?>
    <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
            <session-factory>
                    <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
                    <property name="dialect">NHibernate.Dialect.MsSql2012Dialect</property>
                    <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
                    <property name="connection.connection_string">...</property>
                    <property name="show_sql">true</property>
                    <property name ="current_session_context_class">thread_static</property>
            </session-factory>
    </hibernate-configuration>

Sessionfactory像这样连接起来:

    var configuration = new Configuration();
        configuration.Configure();
        configuration.AddAssembly(Assembly.GetExecutingAssembly());
        _factory = configuration.BuildSessionFactory();
4

2 回答 2

0

您可以尝试使用 AspNetCore 中间件,因为它可以让您访问 HttpContext。

于 2018-04-11T08:32:38.793 回答
0

我找到了一个可行的解决方案,但并不像我希望的那样好。

在我的 Nancy Bootstrapper 中,我添加了一个从 ApplicationStartup 挂钩填充的公共静态属性:

public class Bootstrapper : AutofacNancyBootstrapper
{
    public static IHttpContextAccessor HttpContextAccessor { get; private set; }

    protected override void ApplicationStartup(ILifetimeScope container, IPipelines pipelines)
    {
        HttpContextAccessor = container.Resolve<IHttpContextAccessor>();
    }
}

然后我创建了一个CurrentSessionContext我刚刚调用的新自定义CoreSessionContextMapBasedSessionContext它像所做的那样扩展了抽象WebSessionContext,然后我通过访问 Bootstrapper 上的静态属性将其注入到构造函数中。HttpContextAccessor

public class CoreSessionContext : MapBasedSessionContext
{
    private IHttpContextAccessor _httpContextAccessor;
    private const string SessionFactoryMapKey = "NHibernate.Context.WebSessionContext.SessionFactoryMapKey";

    public CoreSessionContext(ISessionFactoryImplementor factory) : base(factory)
    {
        _httpContextAccessor = Bootstrapper.HttpContextAccessor;
    }

    protected override IDictionary GetMap()
    {
        return _httpContextAccessor.HttpContext.Items[SessionFactoryMapKey] as IDictionary;
    }

    protected override void SetMap(IDictionary value)
    {
        _httpContextAccessor.HttpContext.Items[SessionFactoryMapKey] = value;
    }
}

我做的最后一件事是current_session_context_class从文件中删除元素hibernate.cfg.xml,然后SessionFactory在第三行中将其与我的自定义会话上下文连接起来:

        var configuration = new Configuration();
        configuration.Configure();
        configuration.CurrentSessionContext<CoreSessionContext>();
        configuration.AddAssembly(Assembly.GetExecutingAssembly());
        _factory = configuration.BuildSessionFactory();

然后我就可以使用HttpContextAspNetCore 提供的了。

没有我想要的那么漂亮,但它确实有效。

于 2018-04-11T18:22:46.490 回答