2

我正在使用 Nhibernate 和通用存储库开发一个 .NET Web API 应用程序。现在我正在尝试使用 Ninject 正确设置依赖注入。但是,我的当前配置存在一些问题:有时我的 NHibernate ISession(在下面的 UnitOfWork.cs 中)对象为空或在发出向下到 DAL 并尝试从数据库获取数据的请求时已经关闭。

我无法弄清楚为什么会发生这种情况或我的代码有什么问题。我认为我的 Ninject 范围/绑定在某种程度上不正确,但无法使其正常工作。

这是我当前的实现(我已经剥离了不相关的代码以减少显示的代码量):

NinjectWebCommon.cs

private static void RegisterServices(IKernel kernel)
{
   UnitOfWorkFactory uow = new UnitOfWorkFactory(
      ConfigurationManager.ConnectionStrings["foo"].ConnectionString,
      Assembly.GetExecutingAssembly());

   kernel.Bind<IUnitOfWorkFactory>().ToConstant(uow).InSingletonScope();
   kernel.Bind<IUnitOfWork>().ToMethod(f => f.Kernel.Get<IUnitOfWorkFactory().BeginUnitOfWork()).InRequestScope();

   // Services
   kernel.Bind<ICustomerService>().To<CustomerService>().InRequestScope();

   // Repositories
   kernel.Bind(typeof(IRepository<,>)).To(typeof(Repository<,>)).InRequestScope();

   // Used for Basic Auth (uses customer Service)
   kernel.Bind<IPrincipalProvider>().To<MyPrincipalProvider>().InRequestScope();
}

IUnitOfWorkFactory.cs

public interface IUnitOfWorkFactory : IDisposable
{
    IUnitOfWork BeginUnitOfWork();
    void EndUnitOfWork(IUnitOfWork unitOfWork);
}

UnitOfWorkFactory.cs

public class UnitOfWorkFactory : IUnitOfWorkFactory
{
    public UnitOfWorkFactory(string connectionString, Assembly assembly)
    {
        var rawCfg = new Configuration();
        rawCfg.SetNamingStrategy(new MsSql2005NamingStrategy());
        var cfg = Fluently
            .Configure(rawCfg)                .Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2005.ConnectionString(connectionString))
            .Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()));

        Configuration = cfg.BuildConfiguration();
        SessionFactory = Configuration.BuildSessionFactory();
    }

    protected ISessionFactory SessionFactory { get; private set; }

    protected Configuration Configuration { get; private set; }

    public IUnitOfWork BeginUnitOfWork()
    {
        return new UnitOfWork(this.SessionFactory.OpenSession());
    }

    public void EndUnitOfWork(IUnitOfWork unitOfWork)
    {
        var nhUnitOfWork = unitOfWork as UnitOfWork;
        if (unitOfWork != null)
        {
            unitOfWork.Dispose();
            unitOfWork = null;
        }
    }

    public void Dispose()
    {
        if (this.SessionFactory != null)
        {
            (this.SessionFactory as IDisposable).Dispose();
            this.SessionFactory = null;
            this.Configuration = null;
        }
    }
}

IUnitOfWork.cs

public interface IUnitOfWork : IDisposable
{
    TEntity GetSingle<TEntity>(Expression<Func<TEntity, bool>> expression) where   TEntity : class;
}

UnitOfWork.cs

public class UnitOfWork : IUnitOfWork
{
   public UnitOfWork(NHiberante.ISession session)
   {
      if (session == null)
      {
         throw new ArgumentNullException("session");
      }
      this.Session = session;
   }

   public NHiberante.ISession Session { get; private set; }

   private IQueryable<TEntity> Set<TEntity>() where  TEntity : class
   {
      return Session.Query<TEntity>();
   }

   public TEntity GetSingle<TEntity>(Expression<Func<TEntity, bool>> expression)  where TEntity : class
   {
      return Set<TEntity>().SingleOrDefault(expression);
   }

   public void Dispose()
   {
      if ( this.Session != null )
      {
         (this.Session as IDisposable).Dispose();
         this.Session = null;
      }
   }
}

IRepository.cs

public interface IRepository<TEntity, TPrimaryKey> where TEntity : class
{
    TEntity GetSingle(Expression<Func<TEntity, bool>> expression);
}

存储库.cs

public class Repository<TEntity, TPrimaryKey> : IRepository<TEntity, TPrimaryKey> where TEntity : class
{
   public Repository(IUnitOfWork unitOfWork)
   {
      if (unitOfWork == null)
      {
         throw new ArgumentNullException("unitOfWork");
      }
      this.UnitOfWork = unitOfWork;
   }

   protected IUnitOfWork UnitOfWork { get; private set; }

   public virtual TEntity GetSingle(Expression<Func<TEntity, bool>> expression)
   {
      return UnitOfWork.GetSingle(expression);
   }
}

客户服务.cs

public interface ICustomerService
{
    Customer GetCustomer(string id);
}

客户服务

public class CustomerService : ICustomerService
{
    private readonly IRepository<Customer, string> _customerRepo;

    public CustomerService(IRepository<Customer, string> customerRepo)
    {
        _customerRepo = customerRepo;
    }

    public Customer GetCustomer(string id)
    {
        return _customerRepo.GetSingle(l => l.ID == id);
    }
}

客户控制器.cs

public class CustomerController : ApiController
{
    private ICustomerService _customerService;

    public CustomerController(ICustomerService customerService)
    {
        _customerService = customerService;
    }

    public string Get(string id)
    {
        var customer = _customerService.GetCustomer(id);
        return customer.Name;
    }
}

总结一下:我提出了一个简单的 GetCustomer 请求。CustomerController 被注入了一个 CustomerService 实例。然后将 CustomerService 注入一个 Repository 实例,并将存储库本身注入一个 UnitOfWork 实现(由 UnitOfWorkFactory 类中的 BeginUnitOfWork() 方法创建)。另外值得一提的是,请求首先被身份验证委托处理程序拦截(用于基本身份验证)。此处理程序还使用 CustomerService。

当向 API 发出请求时(通过 REST 客户端或 cURL 或其他),这最初是有效的,但时不时地(或在稍后的某些后续请求中),当我尝试访问 ISession 时,我在数据层中遇到错误对象(为NULL),我必须重新启动服务器才能让它再次工作。

我错过了什么明显的东西吗?谁能解释如何解决这个问题?谢谢!

更新

我一直在进一步调试,发现我的 UnitOfWork 在每次请求时都被正确实例化,从而获得了一个新的 ISession。但在某些情况下,UoW Dispose() 方法会被触发两次(由于根据堆栈跟踪进行了一些 NHibernate 缓存/修剪)。这就是内部会话对象为空的原因。一旦触发了这个异常,在所有后续请求中,Ninject 显然会找到一个已经存在的 UnitOfWork 实例,以及这个空会话。:/

4

1 回答 1

1

ISession对象不是由 ninject 管理,而是由您的示例中的 NH 管理。因此,您IUnitOfWork可以比 NH 管理的会话更长寿。使用 Ninject 管理所有内容或使用适当的会话上下文。问题的根本原因在于BeginUnitOfWork您更新工作单元和使用的位置SessionFactory.OpenSession()

于 2013-02-28T21:43:43.307 回答