基于EF4.1的异构数据库访问组件(二)

接上篇

基于EF4.1的异构数据库访问组件(一) 中已经完成了几点工作:

  • IDbContextStorage – DbContext仓库接口 ;
  • WebDbContextStorage - 用于WEB应用程序的DbContext接口(Http请求结束后关闭数据库连接);
  • IDbContextBuilder,DbContextBuilder – DbContext的动态组建器,用于动态地从各个所要组建DbContext的目标DLL的目标命名空间下读取业务对象的映射配置类,添加至DbModelBuilder的配置容器中;另从Web.Config中读取ConnectionString,准备数据库连接;(如果需要加密ConnectionString,只需要修改DbContextBuilder中读取ConnectionString的代码);

    那么接下来的需求是:在应用程序开始时,即Application_Start事件中,我们需要进行IDbContextStorage的初始化。OK,DbContext仓库有了,DbContext组建器也有了,接下来,就需要一个DbContext的管理器来实现初始化等功能了。

DbContext管理器:DbContextManager

    到底这个管理器要怎么实现呢,我是这样进行分析的:

  • DbContextManager应该是一个静态类;
  • 它应该有一个静态字段:private static IDbContextStorage _storage,在应用程序始初化后,这个字段就相当于一个全局变量,来存储当前应用程序中初始化时动态创建的DbContext;
  • 它应该有一个静态字段: private static Dictionary<string,IDbContextBuilder<DbContext>> _dbContextBuilders。如果应用程序中有多个数据库,那么配置文件也会有多个ConnectionString,这样子的话,应用程序初始化的时候也需要多个DbContextBuilder去动态创建。正好,那就用ConnectionStringName当作KEY,用于标识(IDbContextStorage中获取或添加DbContext时,也是采用ConnectionStringName作为KEY);
  • 它应该有一个方法用来初始化_storage;
  • 它应该有一个方法用来初始化dbContext,并将dbContext添加至_storage中;
  • 它应该还有些方法:1.根据KEY得到当前_storage中的某个DbContext;2.获取_storage中的所有DbContext;。。

    直接上代码:

public static class DbContextManager
{
    // 线程锁
    private static object _syncLock = new object();
    // DbContext仓库
    private static IDbContextStorage _storage;
    // DbContext组装器容器
    private static Dictionary<string, IDbContextBuilder<DbContext>> _dbContextBuilders =
        new Dictionary<string, IDbContextBuilder<DbContext>>();

    /// <summary>
    /// 初始化ˉDbContext仓库
    /// </summary>
    public static void InitStorage(IDbContextStorage storage)
    {
        if (storage == null)
            throw new ArgumentNullException("storage");

        if (_storage != null && _storage != storage)
            throw new ApplicationException("应用程序中已存在DbContextStorage,请确认是否重新初始化");

        _storage = storage;
    }

    /// <summary>
    /// 初始化DbContext
    /// </summary>
    /// <param name="connectionStringName">连接字符串名称</param>
    /// <param name="mappingAssemblyPath">映射配置类所在DLL路径</param>
    /// <param name="mappingNamespace">映射配置类所在命名空间</param>
    public static void Init(string connectionStringName, string mappingAssemblyPath, string mappingNamespace)
    {
        if (string.IsNullOrEmpty(connectionStringName))
            throw new ArgumentNullException("connectionStringName");
        if (string.IsNullOrEmpty(mappingAssemblyPath))
            throw new ArgumentNullException("mappingAssemblyPath");
        if (string.IsNullOrEmpty(mappingNamespace))
            throw new ArgumentNullException("mappingNamespace");

        lock (_syncLock)
        {
            _dbContextBuilders.Add(connectionStringName,
                new DbContextBuilder<DbContext>(connectionStringName, mappingAssemblyPath, mappingNamespace));
        }
    }

    /// <summary>
    /// 根据KEY获取DbContext
    /// </summary> 
    internal static DbContext CurrentByKey(string key)
    {
        if (string.IsNullOrEmpty(key))
            throw new ArgumentNullException("key");
        if (_storage == null)
            throw new ApplicationException("DbContextStorage尚未初始化!");

        DbContext context;
        lock (_syncLock)
        {
            if (!_dbContextBuilders.ContainsKey(key))
                throw new ApplicationException("DbContextBuilders中没有该KEY对应的组装器");

            context = _storage.GetByKey(key);
            if (context == null)
            {
                context = _dbContextBuilders[key].BuildDbContext();
                _storage.SetByKey(key, context);
            }
        }
        return context;
    }

    /// <summary>
    /// 关闭所有数据库连接
    /// </summary>
    internal static void CloseAllDbContexts()
    {
        foreach (var dbContext in _storage.GetAllDbContexts())
        {
            if (dbContext.Database.Connection.State == System.Data.ConnectionState.Open)
                dbContext.Database.Connection.Close();
        }
    }
}

  

    OK,接下来,我们可以进行初始化了,如下图所示:

   

    配置文件:

  <connectionStrings>
    <add name="UserDB" providerName="System.Data.SqlClient" connectionString="server=.;database=UserDb;Integrated Security=SSPI;"/>
    <add name="ProductDB" providerName="System.Data.SqlClient" connectionString="server=.;database=UserDb;Integrated Security=SSPI;"/>
  </connectionStrings>

    无标题

    如上图所示:

  • 系统中存在2个数据库:ProductDb,UserDb
  • 实体:Product(属于ProductDb),UserInfo(属于UserDb)
  • 映射文件:ProductMap,UserInfoMap

 

    初始化顺序是这样子的:

  • 实例化IDbContextStorage;
  • DbContextManager初始化IDbContextStorage;
  • DbContextManager初始化数据库UserDB,UserDb数据库相关的业务对象的映射配置类所在的DLL及命名空间,以及ConnectionStringName
  • DbContextManager初始化数据库ProductDB,ProductDB数据库相关的业务对象的映射配置类所在的DLL及命名空间,以及ConnectionStringName

    在这里需要说明的一点是:为什么要加一个命名空间呢?

    其实,按理来说,DbContextBuilder动态创建DbContext的时候是可以全部扫描LGNet.Data.dll下所有的映射配置类,并加载至其配置容器中,这样做也没有错,但是当实体所对应的映射配置类数量非常多时(大点的项目,实体500个不多吧),若每初始化一个DbContext加载全部Mapping,那么性能就会较慢,虽然慢不了多少。这个就有个较好的比喻,“一分钟有多长,看你是在厕所外面还是在厕所里面”,当在业务高峰期出现故障紧急维护部署时,你就在厕所外面了。     

下一篇

    终于初始化完了,大笑,呵呵,其实很简单吧。。最核心的三个东东:

  • IDbContextStorage - 存放DbContext的
  • DbContextBuilder  - 动态创建DbContext的
  • DbContextManager  - 管理全局DbContext的

 

    差不多又到休息时间了,基本上这个组件的核心功能已经完成了,已经可以初始化动态DbContext了,有些童鞋可能已经知道怎样去提供统一数据接口了。没错,下一篇就是:

  • IRepository<T>接口与EFRepository<T>的实现
  • UnitOfWork的实现

       

posted @ 2011-10-20 23:02  青砖绿树  阅读(3390)  评论(11编辑  收藏  举报