基于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的实现