我正在使用 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 实例,以及这个空会话。:/