Я разрабатываю приложение .NET Web API, используя Nhibernate и общий репозиторий. Теперь я пытаюсь правильно настроить инъекцию зависимости, используя Ninject. Тем не менее, у меня есть некоторые проблемы с моей текущей конфигурацией: иногда мой объект NHibernate ISession (в UnitOfWork.cs ниже) является либо нулевым, либо уже закрытым при подаче запроса, который переходит к DAL, и пытается извлечь данные из базы данных.Настройка Ninject для общего хранилища с использованием Nhibernate
Я не смог точно выяснить, почему это происходит или что не так в моем коде. Я думал, что мой Ninject scoping/binding был чем-то неправильным, но не может заставить его работать.
Это моя текущая реализация (я отбрасываюсь ненужный код, чтобы уменьшить количество коды отображается):
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);
}
Repository.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);
}
}
ICustomerService.cs
public interface ICustomerService
{
Customer GetCustomer(string id);
}
CustomerService
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);
}
}
CustomerController.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 вводится экземпляр репозитория, а сам репозиторий внедряется в реализацию UnitOfWork (который создается методом BeginUnitOfWork() в классе UnitOfWorkFactory). Также стоит упомянуть, что запрос сначала перехватывается обработчиком делегирования аутентификации (для базовой аутентификации). Этот обработчик также использует CustomerService.
При обращении к API (через клиент REST или cURL или что-то еще) это изначально работает, но время от времени (или при последующем последующем запросе) я получаю сообщение об ошибке на уровне данных при попытке для доступа к объекту ISession (NULL), и я должен перезапустить сервер, чтобы он снова работал.
Я пропустил что-то очевидное? Может ли кто-нибудь объяснить, как это решить? Благодаря!
Update
я отладки это еще и выяснил, что мой UnitOfWork правильно инициализируется в каждом запросе и тем самым получить новый ISession. Но в некоторых случаях метод UoW Dispose() запускается дважды (из-за некоторого кэширования/обрезки NHibernate в соответствии с трассировкой стека). Вот почему внутренний объект сеанса имеет значение null. Как только это исключение инициируется, во всех последующих запросах Ninject, очевидно, находит уже существующий экземпляр UnitOfWork с этим нулевым сеансом. :/
Как эта проблема специфична? Вы не используете ninject для создания экземпляра сеанса. Для WCF, REST и т. Д. Вам нужно использовать конкретный контекст сеанса: см. Http://nhforge.org/wikis/reference2-0en/context-sessions.aspx или создать собственный. Также см. Мой пост об общих репозиториях: http://www.planetgeek.ch/2012/05/05/what-is-that-all-about-the-repository-anti-pattern/ реализация может быть даже упрощена с помощью TransactionScope , –
Я думал, что это было специфично для Ninject в том смысле, что Ninject будет вводить интерфейс IUnitOfWork с помощью реализации UnitOfWork, который принимает экземпляр ISession в ctor. Это управляется следующим: 'kernel.Bind() .ToMethod (е => f.Kernel.Get
anve