【问题标题】:NHibernate in Web API ASP.NET: No session bound to the current contextWeb API ASP.NET 中的 NHibernate:没有会话绑定到当前上下文
【发布时间】:2015-03-28 04:36:58
【问题描述】:

我是 NHibernate 的新手,并试图在 ASP.NET WEB API 中使用它。首先,我成功地将它与一个名为“Category”的表一起使用,控制器类如下:

using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using TestMVCProject.Web.Api.HttpFetchers;
using TestMVCProject.Web.Api.Models;
using TestMVCProject.Web.Api.TypeMappers;
using TestMVCProject.Web.Common;
//using TestMVCProject.Web.Common.Security;
using NHibernate;

namespace TestMVCProject.Web.Api.Controllers
{
    [LoggingNHibernateSession]
    public class CategoryController : ApiController
    {
        private readonly ISession _session;
        private readonly ICategoryMapper _categoryMapper;
        private readonly IHttpCategoryFetcher _categoryFetcher;

        public CategoryController(
            ISession session,
            ICategoryMapper categoryMapper,
            IHttpCategoryFetcher categoryFetcher)
        {
            _session = session;
            _categoryMapper = categoryMapper;
            _categoryFetcher = categoryFetcher;
        }

        public IEnumerable<Category> Get()
        {
            return _session
                .QueryOver<Data.Model.Category>()
                .List()
                .Select(_categoryMapper.CreateCategory)
                .ToList();
        }

        public Category Get(long id)
        {
            var category = _categoryFetcher.GetCategory(id);
            return _categoryMapper.CreateCategory(category);
        }


        public HttpResponseMessage Post(HttpRequestMessage request, Category category)
        {
            var modelCategory = new Data.Model.Category
            {
                Description = category.Description,
                CategoryName = category.CategoryName
            };

            _session.Save(modelCategory);

            var newCategory = _categoryMapper.CreateCategory(modelCategory);

            //var href = newCategory.Links.First(x => x.Rel == "self").Href;

            var response = request.CreateResponse(HttpStatusCode.Created, newCategory);
            //response.Headers.Add("Location", href);

            return response;
        }


        public HttpResponseMessage Delete()
        {
            var categories = _session.QueryOver<Data.Model.Category>().List();
            foreach (var category in categories)
            {
                _session.Delete(category);
            }

            return new HttpResponseMessage(HttpStatusCode.OK);
        }


        public HttpResponseMessage Delete(long id)
        {
            var category = _session.Get<Data.Model.Category>(id);
            if (category != null)
            {
                _session.Delete(category);
            }

            return new HttpResponseMessage(HttpStatusCode.OK);
        }


        public Category Put(long id, Category category)
        {
            var modelCateogry = _categoryFetcher.GetCategory(id);

            modelCateogry.CategoryName = category.CategoryName;
            modelCateogry.Description = category.Description;

            _session.SaveOrUpdate(modelCateogry);

            return _categoryMapper.CreateCategory(modelCateogry);
        }
    }
}

但是当我添加具有类别表外键的“产品”表时,产品控制器不起作用并抛出以下异常:

没有会话绑定到当前上下文

ProductController类如下:

using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using TestMVCProject.Web.Api.HttpFetchers;
using TestMVCProject.Web.Api.Models;
using TestMVCProject.Web.Api.TypeMappers;
using TestMVCProject.Web.Common;
//using TestMVCProject.Web.Common.Security;
using NHibernate;

namespace TestMVCProject.Web.Api.Controllers
{
    [LoggingNHibernateSession]
    public class ProductController : ApiController
    {
        private readonly ISession _session;
        private readonly IProductMapper _productMapper;
        private readonly IHttpProductFetcher _productFetcher;


        public ProductController(
            ISession session,
            IProductMapper productMapper,
            IHttpProductFetcher productFetcher)
        {
            _session = session;
            _productMapper = productMapper;
            _productFetcher = productFetcher;
        }

        public IEnumerable<Product> Get()
        {
            return _session
                .QueryOver<Data.Model.Product>()
                .List()
                .Select(_productMapper.CreateProduct)
                .ToList();
        }

        public Product Get(long id)
        {
            var product = _productFetcher.GetProduct(id);
            return _productMapper.CreateProduct(product);
        }


        public HttpResponseMessage Post(HttpRequestMessage request, Product product)
        {
            var modelProduct = new Data.Model.Product
            {
                Description = product.Description,
                ProductName = product.ProductName
            };

            _session.Save(modelProduct);

            var newProduct = _productMapper.CreateProduct(modelProduct);

            //var href = newproduct.Links.First(x => x.Rel == "self").Href;

            var response = request.CreateResponse(HttpStatusCode.Created, newProduct);
            //response.Headers.Add("Location", href);

            return response;
        }


        public HttpResponseMessage Delete()
        {
            var categories = _session.QueryOver<Data.Model.Product>().List();
            foreach (var product in categories)
            {
                _session.Delete(product);
            }

            return new HttpResponseMessage(HttpStatusCode.OK);
        }


        public HttpResponseMessage Delete(long id)
        {
            var product = _session.Get<Data.Model.Product>(id);
            if (product != null)
            {
                _session.Delete(product);
            }

            return new HttpResponseMessage(HttpStatusCode.OK);
        }


        public Product Put(long id, Product product)
        {
            var modelProduct = _productFetcher.GetProduct(id);

            modelProduct.ProductName = product.ProductName;
            modelProduct.Description = product.Description;

            _session.SaveOrUpdate(modelProduct);

            return _productMapper.CreateProduct(modelProduct);
        }
    }
}

以及 Product 表的映射类:

using TestMVCProject.Data.Model;
using FluentNHibernate.Mapping;

namespace TestMVCProject.Data.SqlServer.Mapping
{
    public class ProductMap : ClassMap<Product>
    {
        public ProductMap()
        {
            Id(x => x.ProductId);
            Map(x => x.ProductName).Not.Nullable();
            Map(x => x.Description).Nullable();
            Map(x => x.CreateDate).Not.Nullable();
            Map(x => x.Price).Not.Nullable();               

            References<Category>(x => x.CategoryId).Not.Nullable();               
        }
    }
}

怎么了?

【问题讨论】:

    标签: asp.net nhibernate asp.net-web-api fluent-nhibernate nhibernate-configuration


    【解决方案1】:

    你的 sn-ps 丢失了方法,ISessionFactory 是如何创建的,ISession 是如何传递到你的控制器中的......你应该关注这个非常全面的故事 (by Piotr Walat):

    NHibernate session management in ASP.NET Web API

    在哪里可以看到我们,可以使用2.3. Contextual Sessions

    NHibernate.Context.WebSessionContext - 将当前会话存储在 HttpContext 中。您负责使用 CurrentSessionContext 类的静态方法绑定和取消绑定 ISession 实例。

    配置

    <session-factory>
        ..
        <property name="current_session_context_class">web</property>
    </session-factory>
    

    在文章中你可以检查我们是否需要在应用启动时初始化工厂(只是一个摘录)

    public class WebApiApplication : System.Web.HttpApplication  
    {
        private void InitializeSessionFactory() { ... }
    
        protected void Application_Start()
        {
            InitializeSessionFactory();
        ...
    

    接下来我们应该创建一些 AOP 过滤器(只是一个摘录)

    public class NhSessionManagementAttribute : ActionFilterAttribute  
    {
        ...
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            // init session
            var session = SessionFactory.OpenSession();
            ...
    
        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            // close session
            ...
            session = CurrentSessionContext.Unbind(SessionFactory);
        }
    

    更多详情请查看source mentioned above

    【讨论】:

      【解决方案2】:

      您将会话传递给控制器​​工厂的构造函数的方法似乎不起作用,有几种方法可以做到这一点

      1.使用依赖注入

      如果你使用依赖注入框架,你必须配置控制器,以便根据请求构建它,它应该看起来像这样(我使用了Ninject的代码)

      第 1 步 - 设置注入会话

      public class DIModule : NinjectModule
      {
          public override void Load()
          {
          this.Bind<ISessionFactory>()... bind to the session factory
              this.Bind<ISession>().ToMethod(ctx => ctx.Kernel.Get<ISessionFactory>().OpenSession())
                  .InRequestScope();
          }
      
          private ISession CreateSessionProxy(IContext ctx)
          {
              var session = (ISession)this.proxyGenerator.CreateInterfaceProxyWithoutTarget(typeof(ISession), new[] { typeof(ISessionImplementor) }, ctx.Kernel.Get<SessionInterceptor>());
              return session;
          }
      }
      

      第 2 步 - 创建控制器工厂,以便在解析时注入会话

      public class NinjectControllerFactory : DefaultControllerFactory, IDependencyResolver
          {
              private IDependencyResolver _defaultResolver;
      
              public NinjectControllerFactory(IDependencyResolver defaultResolver)
              {
                  _defaultResolver = defaultResolver;
              }
      
              protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
              {
                  return controllerType == null
                         ? null
                         : (IController)DependencyKernel.Kernel.Get(controllerType);
              }
      
              public IDependencyScope BeginScope()
              {
                  return this;
              }
      
              public object GetService(Type serviceType)
              {
                  try
                  {
                      return DependencyKernel.Kernel.Get(serviceType);
                  }
                  catch (Exception)
                  {
                      return GetService(serviceType);
                  }
              }
      
              public IEnumerable<object> GetServices(Type serviceType)
              {
                  try
                  {
                      object item = DependencyKernel.Kernel.Get(serviceType);
                      return new List<object>() {item};
                  }
                  catch (Exception)
                  {
                      return GetServices(serviceType);
                  }
              }
      
              public void Dispose()
              {
              }
          }
      

      第 3 步 - 注册控制器工厂

      public class MvcApplication : System.Web.HttpApplication
          {
              protected void Application_Start()
              {
      
                  var factory = new NinjectControllerFactory(GlobalConfiguration.Configuration.DependencyResolver);
                  ControllerBuilder.Current.SetControllerFactory(factory);
                  GlobalConfiguration.Configuration.DependencyResolver = factory;
              }
          }
      

      现在会发生的是,当您的控制器被创建时,它将为每个请求注入一个新的 NH 会话。

      2。使用过滤器

      这要简单得多,但您可能需要稍微更改一下控制器才能工作,

      第 1 步 - 为工厂设置正确的会话上下文

      _sessionFactory = CreateConfiguration()
                      .ExposeConfiguration(c => c.SetProperty("current_session_context_class","web"))
                      .BuildSessionFactory();
      

      第 2 步 - 创建过滤器

      public class SessionPerRequestAttribute : ActionFilterAttribute
          {
              public override void OnActionExecuting(HttpActionContext actionContext)
              {
                  var session = SessionFactory.OpenSession();
                  NHibernate.Context.CurrentSessionContext.Bind(session);
                  base.OnActionExecuting(actionContext);
              }
      
              public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
              {
                  var session = SessionFactory.GetCurrentSession();
                  session.Flush();
                  session.Clear();
                  session.Close();
                  base.OnActionExecuted(actionExecutedContext);
              }
          }
      

      第 3 步 - 在全局配置中注册过滤器

      public static class WebApiConfig
          {
              public static void Register(HttpConfiguration config)
              {
                  //Do other config here
                  config.Filters.Add(new SessionPerRequestAttribute());
              }
          }
      

      第 4 步 - 稍微修改一下你的控制器,

      public class CategoryController : ApiController
      {
          private readonly ICategoryMapper _categoryMapper;
          private readonly IHttpCategoryFetcher _categoryFetcher;
      
          public CategoryController(
              ICategoryMapper categoryMapper,
              IHttpCategoryFetcher categoryFetcher)
          {
              _categoryMapper = categoryMapper;
              _categoryFetcher = categoryFetcher;
          }
      
          public IEnumerable<Category> Get()
          {
              var session = SessionFactory.GetCurrentSession();
              return session 
                  .QueryOver<Data.Model.Category>()
                  .List()
                  .Select(_categoryMapper.CreateCategory)
                  .ToList();
          }
      }
      

      这里发生的情况是,当一个请求到来时,它将创建一个新会话,并绑定到请求上下文,同样用于 Web API 方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-12-24
        • 1970-01-01
        • 1970-01-01
        • 2014-03-09
        • 2011-04-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多