Web API中的NHibernate ASP.NET:没有绑定到当前上下文的会话

时间:2015-03-28 04:36:59

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

我是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);
        }
    }
}

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

  

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

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 table的映射类:

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();               
        }
    }
}

有什么问题?

2 个答案:

答案 0 :(得分:3)

您的代码片段缺失方式,ISessionFactory如何创建以及ISession如何传递到您的控制器......您应该遵循这个非常全面的故事(由 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

答案 1 :(得分:1)

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

<强> 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方法。