2

Nhibernateと汎用リポジトリを使用して.NETWebAPIアプリケーションを開発しています。現在、Ninjectを使用して依存性注入を正しく設定しようとしています。ただし、現在の構成にいくつかの問題があります。DALにダウンしてデータベースからデータをフェッチしようとする要求を行うときに、NHibernate ISession(以下のUnitOfWork.cs内)オブジェクトがnullになるか、すでに閉じられている場合があります。

なぜこれが発生するのか、コードの何が問題なのかを正確に把握することはできませんでした。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);
}

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

顧客サービス

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にRepositoryインスタンスが注入され、リポジトリ自体にUnitOfWork実装(UnitOfWorkFactoryクラスのメソッドBeginUnitOfWork()によって作成される)が注入されます。また、要求は最初に認証委任ハンドラー(基本認証用)によってインターセプトされることにも言及する価値があります。このハンドラーもCustomerServiceを使用します。

APIに(RESTクライアントやcURLなどを介して)リクエストを行うと、これは最初は機能しますが、時々(またはその後のリクエストで)、ISessionにアクセスしようとするとデータレイヤーでエラーが発生しますオブジェクト(NULL)であり、サーバーを再起動して再び機能させる必要があります。

明らかな何かを見逃したことがありますか?誰かがこれを解決する方法を説明できますか?ありがとう!

アップデート

私はこれをさらにデバッグしていて、UnitOfWorkがすべてのリクエストで適切にインスタンス化され、新しいISessionを取得していることを発見しました。ただし、場合によっては、UoW Dispose()メソッドが2回トリガーされます(スタックトレースによるNHibernateのキャッシング/プルーニングが原因)。これが、内部セッションオブジェクトがnullである理由です。この例外がトリガーされると、後続のすべてのリクエストで、Ninjectは明らかにこのnullセッションでUnitOfWorkの既存のインスタンスを検出します。:/

4

1 に答える 1

1

ISessionオブジェクトはninjectではなく、サンプルのNHによって管理されます。したがって、IUnitOfWorkNHが管理するセッションよりも長生きできます。Ninjectですべてを管理するか、適切なセッションコンテキストを使用してください。問題の根本的な原因は、BeginUnitOfWork作業単位を更新して使用する場所にありますSessionFactory.OpenSession()

于 2013-02-28T21:43:43.307 に答える