2013-02-25 2 views
2

Я разрабатываю приложение .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 с этим нулевым сеансом. :/

+0

Как эта проблема специфична? Вы не используете 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 , –

+0

Я думал, что это было специфично для Ninject в том смысле, что Ninject будет вводить интерфейс IUnitOfWork с помощью реализации UnitOfWork, который принимает экземпляр ISession в ctor. Это управляется следующим: 'kernel.Bind () .ToMethod (е => f.Kernel.Get anve

ответ

1

Объект ISession не управляется ninject, а NH в вашем образце. Поэтому ваш IUnitOfWork может жить дольше, чем сеанс, управляемый NH. Либо управляйте всем с помощью Ninject, либо используйте соответствующий контекст сеанса. Основная причина проблемы - BeginUnitOfWork, где вы новичок в своей работе и используете SessionFactory.OpenSession().

Смежные вопросы