4

我正在尝试解决如何在 ASP.NET Web 应用程序中完成存储库模式的实现。

目前,我为每个域类定义了一个存储库接口,用于定义例如加载和保存该类的实例的方法。

每个存储库接口都由一个执行 NHibernate 内容的类实现。Castle Windsor根据web.config将类的DI整理到界面中。下面提供了一个已实现类的示例:

  public class StoredWillRepository : IStoredWillRepository
  {
    public StoredWill Load(int id)
    {
      StoredWill storedWill;
      using (ISession session = NHibernateSessionFactory.OpenSession())
      {
        storedWill = session.Load<StoredWill>(id);
        NHibernateUtil.Initialize(storedWill);
      }
      return storedWill;
    }

    public void Save(StoredWill storedWill)
    {
      using (ISession session = NHibernateSessionFactory.OpenSession())
      {
        using (ITransaction transaction = session.BeginTransaction())
        {
          session.SaveOrUpdate(storedWill);
          transaction.Commit();
        }
      }
    }
  }

正如在前一个线程中所指出的,存储库类需要接受一个工作单元容器(即 ISession),而不是在每个方法中实例化它。

我预计工作单元容器将在需要时由每个 aspx 页面创建(例如,在属性中)。

然后,当 Windsor 为我创建这个工作单元容器实例时,如何指定将其传递到 StoredWillRepository 的构造函数中?

还是这种模式完全错误?

再次感谢您的建议。

大卫

4

3 回答 3

1

我有一个建立在 NHibernate 之上的持久性框架,用于一些 Web 应用程序。它使用 Unity 提供的具体实例将 NH 实现隐藏在一个IRepositoryIRepository<T>接口后面(因此,理论上我可以很容易地将 NHibernate 换成实体框架)。

由于 Unity 不(或者至少我使用的版本不)支持传入除了依赖注入本身之外的构造函数参数,因此无法传入现有的 NH ISession;但我确实希望 UOW 中的所有对象共享相同的 ISession。

我通过拥有一个控制存储库类来解决这个问题,该存储库类在每个线程的基础上管理对 ISession 的访问:

    public static ISession Session
    {
        get
        {
            lock (_lockObject)
            {
                // if a cached session exists, we'll use it
                if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
                {
                    return (ISession)PersistenceFrameworkContext.Current.Items[NHibernateRepository.SESSION_KEY];
                }
                else
                {
                    // must create a new session - note we're not caching the new session here... that's the job of
                    // BeginUnitOfWork().
                    return _factory.OpenSession(new NHibernateInterceptor());
                }
            }
        }
    }

在此示例中,如果不在 Web 上下文中,或者如果它在 Web 上下文中(以避免线程池问题) ,则PersistenceFrameworkContext.Current.Items访问IList<object>存储在其中的对象。对属性的第一次调用从存储的工厂实例中实例化,后续调用只是从存储中检索它。锁定会稍微减慢速度,但不如锁定 appdomain 范围的静态实例。ThreadStaticHttpContext.Current.ItemsISessionISession

然后我有BeginUnitOfWork方法EndUnitOfWork来照顾 UOW - 我特别禁止嵌套 UOW,因为坦率地说,它们管理起来很痛苦。

    public void BeginUnitOfWork()
    {
        lock (_lockObject)
        {
            if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
                EndUnitOfWork();

            ISession session = Session;
            PersistenceFrameworkContext.Current.Items.Add(SESSION_KEY, session);
        }
    }

    public void EndUnitOfWork()
    {
        lock (_lockObject)
        {
            if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
            {
                ISession session = (ISession)PersistenceFrameworkContext.Current.Items[SESSION_KEY];
                PersistenceFrameworkContext.Current.Items.Remove(SESSION_KEY);
                session.Flush();
                session.Dispose();
            }
        }
    }

最后,一对方法提供对特定于域类型的存储库的访问:

    public IRepository<T> For<T>()
        where T : PersistentObject<T>
    {
        return Container.Resolve<IRepository<T>>();
    }

    public TRepository For<T, TRepository>()
        where T : PersistentObject<T>
        where TRepository : IRepository<T>
    {
        return Container.Resolve<TRepository>();
    }

(这里PersistentObject<T>是一个提供 ID 和 Equals 支持的基类。)

因此,对给定存储库的访问处于模式中

NHibernateRepository.For<MyDomainType>().Save();

然后将其覆盖,以便您可以使用

MyDomainType.Repository.Save();

如果给定类型有一个专门的存储库(即需要比它可以得到的更多IRepository<T>),那么我创建一个派生自的接口IRepository<T>,一个继承自我的实现的扩展实现IRepository<T>,并且在域类型本身中,我Repository使用覆盖静态属性new

    new public static IUserRepository Repository
    {
        get
        {
            return MyApplication.Repository.For<User, IUserRepository>();
        }
    }

MyApplication[在实际产品中被称为不那么古怪的东西]是一个外观类,它负责Repository通过 Unity 提供实例,因此您不依赖于域类中特定的 NHibernate 存储库实现。)

这为我提供了通过 Unity 实现存储库的完全可插拔性、轻松地以代码访问存储库而无需跳过箍,以及透明的每个线程ISession管理。

除了上面的代码之外,还有更多的代码(我已经大大简化了示例代码),但你明白了一般的想法。

MyApplication.Repository.BeginUnitOfWork();
User user = User.Repository.FindByEmail("wibble@wobble.com");
user.FirstName = "Joe"; // change something
user.LastName = "Bloggs";
// you *can* call User.Repository.Save(user), but you don't need to, because...
MyApplication.Repository.EndUnitOfWork();
// ...causes session flush which saves the changes automatically

在我的 Web 应用程序中,我有每个请求的会话,因此分别被调用BeginUnitOfWork和调用。EndUnitOfWorkBeginRequestEndRequest

于 2010-07-23T14:23:24.320 回答
0

我的结构与您的结构非常相似,这就是我解决您问题的方法:

1)要在每个方法上指定我的容器,我有一个单独的类(“ SessionManager ”),然后我通过静态属性调用它。通过这样做,这是一个使用我的 Save 实现的示例:

private static ISession NHibernateSession
{
    get { return SessionManager.Instance.GetSession(); }
}

public T Save(T entity)
{
    using (var transaction = NHibernateSession.BeginTransaction())
    {
        ValidateEntityValues(entity);
        NHibernateSession.Save(entity);

        transaction.Commit();
    }

    return entity;
}

2) 我的容器不是在每个 ASPX 页面上创建的。我在global.asax页面上实例化了我所有的 NHibernate 优点。

**更多的东西涌现**

3)您不需要帮助器来实例化负载。您不妨使用 Get 而不是 Load。更多信息@加载和获取之间的区别

4) 使用您当前的代码,您将不得不为您需要的每个域对象(StoredWillRepository、PersonRepository、CategoryRepository 等?)重复几乎相同的代码,这似乎是一个拖累。您可以很好地使用泛型类在 NHibernate 上进行操作,例如:

public class Dao<T> : IDao<T>
{
    public T SaveOrUpdate(T entity)
    {
        using (var transaction = NHibernateSession.BeginTransaction())
        {
            NHibernateSession.SaveOrUpdate(entity);
            transaction.Commit();
        }

        return entity;
    }
}

在我的实现中,我可以使用类似的东西

Service<StoredWill>.Instance.SaveOrUpdate(will);
于 2010-07-23T12:52:47.070 回答
0

从技术上讲,我的问题的答案是使用 container.Resolve 的重载,它允许您将构造函数参数指定为匿名类型:

IUnitOfWork unitOfWork = [Code to get unit of work];
_storedWillRepository = container.Resolve<IStoredWillRepository>(new { unitOfWork = unitOfWork });

但是让我们面对现实吧,其他所有人提供的答案都提供了更多信息。

于 2010-07-23T16:03:10.400 回答