NHibernate.3.0.Cookbook第三章第3节的翻译

Setting up session per presenter

session-per-presenter模式创建会话

  在桌面应用程序中使用MVP(model-view-presenter)模式, 最好每个presenter使用一个会话. 这种方法也适用于model-view-view-model模式.有关这些模式的更多信息请参考http://en.wikipedia.org/wiki/Model-view-presenter.
  本节介绍如何使用依赖注入来实现session-per-presenter模式.

准备

  你将需要下载Ninject,下载地址http://github.com/remogloor/ninject.extensions.namedscope.
  下载源代码的ZIP包并解压. 打开Ninject.Extensions.NamedScope.sln解决方案并编译.将生成的Ninject.dll和Ninject.Extensions.NamedScope.dll复制到  我们的Cookbook解决方案下的Lib文件夹中.
  如果你对依赖注入的概念还不是很熟悉,可以先看下TekPub上的一个免费视频,地址:http://tekpub.com/view/concepts/1 .

提示

  使用其他的依赖注入框架也可以完成本节的示例. 只需用你中意的DI框架的等价配置去替代NinjectBindings类.

步骤

1.   在解决方案中新添一个名为SessionPerPresenter的控制台项目.
2.   添加引用:Eg.Core项目(第一章中),  NHibernate.dll, NHibernate.ByteCode.Castle.dll, log4net.dll, Ninject.dll, 和 Ninject.Extensions.NamedScope.dll.
3.   添加一个App.config文件并在文件中创建NHibernate和log4net的配置节点.可以参考Configuring NHibernate和Configuring NHibernate logging小节示例.
4.   为该项目添加一个名为Data的文件夹.
5.   在Data文件夹中创建一个IDao<TEntity>接口,代码如下:

View Code
public interface IDao<TEntity> : IDisposable
  where TEntity : class
{
  IEnumerable<TEntity> GetAll();
}

6.   创建一个该接口的实现,代码如下:

View Code
public class Dao<TEntity> : IDao<TEntity>
  where TEntity : class
{
  private readonly ISessionProvider _sessionProvider;
  public Dao(ISessionProvider sessionProvider)
  {
    _sessionProvider = sessionProvider;
  }
  public void Dispose()
  {
    _sessionProvider.Dispose();
  }
 public IEnumerable<TEntity> GetAll()
  {
    var session = _sessionProvider.GetCurrentSession();
    IEnumerable<TEntity> results;
    using (var tx = session.BeginTransaction())
    {
      results = session.QueryOver<TEntity>()
        .List<TEntity>();
      tx.Commit();
    }
    return results;
  }
}

7.   在Data文件夹中创建一个ISessionProvider接口,代码如下:

View Code
public interface ISessionProvider : IDisposable
{
  ISession GetCurrentSession();
  void DisposeCurrentSession();
}

8.   创建该接口的实现,代码如下:

View Code
public class SessionProvider
  : ISessionProvider
{
  private readonly ISessionFactory _sessionFactory;
  private ISession _currentSession;
  public SessionProvider(ISessionFactory sessionFactory)
  {
    Console.WriteLine("Building session provider");
    _sessionFactory = sessionFactory;
  }
  public ISession GetCurrentSession()
  {
    if (null == _currentSession)
      _currentSession = _sessionFactory.OpenSession();
    return _currentSession;
  }
  public void DisposeCurrentSession()
  {
    _currentSession.Dispose();
    _currentSession = null;
  }
  public void Dispose()
  {
    if (_currentSession != null)
      _currentSession.Dispose();
    _currentSession = null;
  }
}

9.   创建一个名为NinjectBindings的Ninject模块,代码如下:

View Code
public class NinjectBindings : NinjectModule
{
  public override void Load()
  {
    const string presenterScope = "PresenterScope";
    var asm = GetType().Assembly;
    var presenters =
      from t in asm.GetTypes()
      where typeof (IPresenter).IsAssignableFrom(t) &&
            t.IsClass && !t.IsAbstract
      select t;
    foreach (var presenterType in presenters)
      Kernel.Bind(presenterType)
        .ToSelf()
        .DefinesNamedScope(presenterScope);
    Kernel.Bind<ISessionProvider>()
      .To<SessionProvider>()
      .InNamedScope(presenterScope);
    Kernel.Bind(typeof(IDao<>))
      .To(typeof(Dao<>));
  }
}

10.  在项目的根目录下,创建一个名为ProductListView的类,代码如下:

View Code
public class ProductListView
{
  private readonly string _description;
  private readonly IEnumerable<Product> _products;
  public ProductListView(
    string description,
    IEnumerable<Product> products)
  {
    _description = description;
    _products = products;
  }
  public void Show()
  {
    Console.WriteLine(_description);
    foreach (var p in _products)
      Console.WriteLine(" * {0}", p.Name);
  }
}

11.  创建一个继承自IDisposable的公共的IPresenter接口. 这个接口先空着.
12.  创建一个名为MediaPresenter的类,代码如下:

View Code
public class MediaPresenter : IPresenter
{
  private readonly IDao<Movie> _movieDao;
  private readonly IDao<Book> _bookDao;
  public MediaPresenter(IDao<Movie> movieDao,
    IDao<Book> bookDao)
  {
    _movieDao = movieDao;
    _bookDao = bookDao;
  }
  public ProductListView ShowBooks()
  {
    return new ProductListView("All Books",
      _bookDao.GetAll().OfType<Product>());
  }
  public ProductListView ShowMovies()
  {
    return new ProductListView("All Movies",
      _movieDao.GetAll().OfType<Product>());
  }
  public void Dispose()
  {
    _movieDao.Dispose();
    _bookDao.Dispose();
  }
}

13.  创建一个名为ProductPresenter的类,代码如下:

View Code
public class ProductPresenter : IPresenter
{
  private readonly IDao<Product> _productDao;
  public ProductPresenter(IDao<Product> productDao)
  {
    _productDao = productDao;
  }
  public ProductListView ShowAllProducts()
  {
    return new ProductListView("All Products",
      _productDao.GetAll());
  }
  public virtual void Dispose()
  {
    _productDao.Dispose();
  }
}

14.  在Program.cs的Main方法中,添加下述代码:

View Code
var nhConfig = new Configuration().Configure();
var sessionFactory = nhConfig.BuildSessionFactory();
var kernel = new StandardKernel();
kernel.Load(new Data.NinjectBindings());
kernel.Bind<ISessionFactory>()
  .ToConstant(sessionFactory);
var media1 = kernel.Get<MediaPresenter>();
var media2 = kernel.Get<MediaPresenter>();
media1.ShowBooks().Show();
media2.ShowMovies().Show();
media1.Dispose();
media2.Dispose();
using (var product = kernel.Get<ProductPresenter>())
{
  product.ShowAllProducts().Show();
}
Console.WriteLine("Press any key");
Console.ReadKey();

15.  如果你愿意的话,可以在NHCookbook数据库中创建一些测试数据:product, book和movie数据
16.  编译运行,结果如下图所示:

原理

  这个示例中有几处有意思的地方. 首先,我们创建了一个稍微复杂的对象图. MediaPresenter的每个实例如下图所示:

  上图中,两个数据访问对象共享一个会话实例provider.这通过依赖注入框架-Ninject的配置来实现
  在NinjectBindings中, 我们通过匹配我们的服务接口来实现他们的匹配. 将泛型接口IDao<>绑定到Dao<>,因此对IDao<Book>的请求将被解析为对Dao<Book>的请求,  IDao<Movie> 和 Dao<Movie>等也是同样的原理.
  通过使用DefinesNamedScope和InNamedScope我们完成了一个session per presenter模式. 我们在程序集中查找所有的IPresenter实现.每个presenter都被绑定并且定义了PresenterScope. 当将ISessionProvider绑定到SessionProviderImpl时,我们使用InNamedScope("PresenterScope")来指明每个presenter只有一个会话provider.
  Kernel.Get<MediaPresenter>()的调用将返回一个新的presenter实例(该实例被完全构造出来以备使用).它将有两个数据访问对象共享一个共同的会话provider . 不在使用该presenter时,要关闭会话和释放数据库连接,此时请确保调用了Dispose().
  在Dao中一个标准的Save方法,其代码如下所示:

View Code
var session = _sessionProvider.GetCurrentSession();
try
{
  session.SaveOrUpdate(entity);
}
catch (StaleObjectStateException)
{
  _sessionProvider.DisposeCurrentSession();
  throw;
}

  请注意在catch块中我们是如何立刻丢弃会话的.当NHibernate从一个内部会话调用中抛出一个异常时,该会话的状态是未定义的.在该会话上您唯一可做的安全操作只剩下:Dispose(). 这使得我们可以轻易的应对任何异常,因为崩溃掉的会话将被丢弃,所以一个新的会话会将其替代.
  应当注意到失败的会话和实体仍然是相关联的. 应该将这些实体附加到一个新的会话,因为对失败会话进行的任何操作,包括延迟加载在内都可能引起更多的异常. 本章后面的session.Merge小节会讨论实现附加的一个方法.

扩展

  与web应用程序相比,在桌面应用程序中边界不太好界定,但是有两种很常见的反模式来处理NHibernate会话.第一个, 单例会话有下述问题:

  • 没有定义将会话flush到数据库的点.
  • 应用程序中不相关的部分之间存在不可测试的交互.
  • 无法从StaleObjectExceptions或其他引起会话崩溃的异常中轻易恢复.
  • 一个带状态的单例模式通常是一个不好的架构.

第二个, 一个微型会话,这样的会话会被打开,执行一个简单的操作后马上关闭.这样的情况下unit of work毫无优势可言,最适合该情况的应该是使用会话缓存(尤其适合从数据库频繁重复抓取实体的情况.).

转载于:https://www.cnblogs.com/carfieldSE/archive/2012/07/13/2590046.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值