博客园  :: 首页  :: 新随笔  :: 联系 :: 管理

C# NHibernate处理多帐套问题

Posted on 2011-06-24 12:49  codingsilence  阅读(207)  评论(0编辑  收藏  举报

久仰ORM大名由来已久,但真正学习ORM才是前两周的事,在网上挑来拣去,终于决定好好学习一下NHibernate(IBatisNet我也比较感兴趣,但是鉴于时间有限,暂时搁置),一来因为其鼻祖Hibernate名气大,二来嘛,因为它是开源的,有机会看看它的源码,呵呵!
    上网搜了好久,没搜到几篇文档,没法子只能啃Hibernate的文档慢慢学习了,不久我就发现一个问题:NHibernate好像不能同时操作几个数据库嘛?假设有这样分布式系统,它有N个客户端,一个应用程序服务器,多个数据库服务器,客户端可以根据自己的配置通过应用程序服务器连接到不同的数据库服务器(应该就是听棠描述的所谓多帐套),NHibernate应该如何处理这种情况呢?如果了解一点NHibernate的配置的话,大概就知道是通过Configuration.BuildSessionFactory获得ISessionFactory接口的,而通过这种方法我们只能利用连接字符串指定一个默认的数据库,在《在Window Form中使用NHibernate》一文中提到可以通过增加一个配置文件的方式连接到其它的数据库,但按照原文的意思,如果要使用第二个数据库,意味着要第二次进行昂贵的BuildSessionFactory调用,而且假如我们允许用户创建删除数据库的话,配置文件实在是不够灵活,那么有什么办法可以达到我们的目的呢?
    查阅NHibernate的doc,无意中发现OpenSession有个overload的方法,可以提供一个IDbConnection的参数,看来老天有眼,直觉告诉我,就是它了!!!经过实验,只要指定这个参数,就可以轻轻松松实现动态改变连接的数据库了!废话少说,看代码……
    首先是SessionFactory的代码,这里将SessionFactory实现为单例,主要是因为SessionFactory建立极为耗时耗力,这样可以避免重复创建。

using System;
using System.Reflection;
using System.Diagnostics;
using System.Data;

using NHibernate;
using NHibernate.Cfg;

namespace MyProject.Services
{
    public class SessionFactory
    {
        private static Configuration _configuration = null;
        private static ISessionFactory _factory = null;

        static SessionFactory()
        {
            _configuration = new Configuration();

            _configuration.SetProperty("hibernate.dialect", "NHibernate.Dialect.MsSql2000Dialect");
            _configuration.SetProperty("hibernate.connection.provider",
                "NHibernate.Connection.DriverConnectionProvider");
            _configuration.SetProperty("hibernate.connection.driver_class",
                "NHibernate.Driver.SqlClientDriver");
            _configuration.SetProperty("hibernate.connection.connection_string",
                @"Server=localhost;initial catalog=master;Integrated Security=SSPI");

            _configuration.AddAssembly(Assembly.Load("MyProject.Beans"));
            _factory = _configuration.BuildSessionFactory();
        }

        public static ISession OpenSession()
        {
            Debug.Assert(_factory != null, "ISessionFactory is null");
            return _factory.OpenSession();
        }

        public static ISession OpenSession(IDbConnection connection)
        {
            Debug.Assert(_factory != null, "ISessionFactory is null");
            Debug.Assert(connection != null, "IDbConnection is null");
            return _factory.OpenSession(connection);
        }

        public static void Initialize()
        {
            // do nothing, just trigger the static .ctor
        }
    }
}

   大家可以发现,我连接的居然master数据库,嘿嘿,不错,这就是奥妙所在了,调用BuildSessionFactory的时候,指定哪个数据库并没有太大关系,你可以指定任何一个数据库都没有问题,当然,这样做的后果就是你必须在OpenSession的时候指定一个连接,否则的话,狠狠,后果自负哦!
    好了,上面就是全部的关键了,接下来就是如何使用的问题了,我这里举个例子,以做抛砖引玉之用:

        public static void TestAddUser(User newUser)
        {
            string connstring = @"Server=localhost;initial catalog=gcs2;Integrated Security=SSPI";
            using (SqlConnection conn = new SqlConnection(connstring))
            {
                conn.Open();
                ISession session = SessionFactory.OpenSession(conn as IDbConnection);
                ITransaction transaction = session.BeginTransaction();
                try
                {
                    session.Save(newUser);
                    transaction.Commit();
                }
                catch (Exception ex)
                {
                    transaction.Rollback();
                    throw ex;
                }
                finally
                {
                    session.Close();
                }
            }
        }

相关的调用代码如下:

        private void btnAdd_Click(object sender, EventArgs e)
        {
            User newUser = new User();
            newUser.Name = "<new user>";
            newUser.Password = "<none>";
            newUser.CreateDate = DateTime.Now;
            newUser.LastLogin = DateTime.Now;
            UserFactory.TestAddUser(newUser);
        }

如果没有问题的话,最终User将被保存到gcs2数据库中,而不是master数据库!事实上,通过改变TestAddUser成员函数中的连接字符串(可以由客户端传过来),你说吧,你想连到哪儿?呵呵!

    当然大家也看到了,强行指定connection是以牺牲代码的平台移植性为代价的。我这里就强行指定了SqlConnection作为实际连接,当然,这个问题很好解决,一个简单工厂就绰绰有余了,好,enjoy it!

注:这是本人第一次发表有关NHibernate的文章,而且本人才疏学浅,如有遗漏或错误之处,请各位指正!