class="p0"> 从本章开始对框架的讲叙开始进入核心类库的讲解,前面都是对框架外在功能讲解,让人有个整体的概念,知道包含哪些功能与对系统开发有什么帮助。以后多章都是讲解核心类库的,讲解的方式基本按照代码的目录结构,这样阅读代码的时候也可以针对性看;还有就是为了更好理解文章中的内容把目前还不够完善的源代码发布,这个版本Winform部分基本可以直接运行,而Web部分与WCF部分的控制器代码没有完成,所以暂时还运行不起来,不过这并不影响学习核心类库,再就是尽快在下个版本发布一个完整版本给大家;
EnterpriseFrameWork框架源代码V1.0下载:http://pan.baidu.com/s/1pJsMLlx
核心类库EFWCoreLib目录结构
EnterpriseFrameWork框架的底层功能是使用微软企业库(EntLib)来实现的,包括使用EntLib的The Data Access Application Block实现数据库访问,The Caching Application Block进行缓存管理,The Exception Handling Application Block进行异常处理,The Logging Application Block进行日志记录,Unity Dependency Injection and Interception依赖注入容器进行对象映射;在这些功能之上再进行了一次封装,让我们再编写代码过程中更简单的运用,还有就算EntLib以后升级也不需要修改系统中的代码;
微软企业库的一些学习资料:
Enterprise Library 5.0.msi安装包:http://pan.baidu.com/s/1gdnDz7l
Enterprise Library 5.0说明文档:http://pan.baidu.com/s/1pJjtvCZ
CHM版说明文档:http://pan.baidu.com/s/1c0rhtba
学习实例代码:http://pan.baidu.com/s/1dDthk3Z
Enterprise Library 5.0源代码:http://pan.baidu.com/s/1mgKDot6
黄聪:Enterprise Library 5.0 系列教程:http://www.cnblogs.com/huangcong/archive/2010/06/08/1753988.html
一、EntLib配置文件
我们先看web项目Web.config中对EntLib的配置,Winform项目中的是App.config
其中标记的红色部分指定了EntLib的完整配置文件Entlib.config,
<configuration> <configSections> <section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" /> <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" /> <section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" /> <section name="cachingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Caching.Configuration.CacheManagerSettings, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" /> </configSections> <dataConfiguration defaultDatabase="SQL2005" /> <connectionStrings> <add name="SQL2005" connectionString="Data Source=.;Initial Catalog=3yxx_Cssd;User ID=sa;pwd=1;" providerName="System.Data.SqlClient" /> </connectionStrings> <exceptionHandling> <exceptionPolicies> <add name="HISPolicy"> <exceptionTypes> <add name="All Exceptions" type="System.Exception, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" postHandlingAction="NotifyRethrow"> <exceptionHandlers> <add name="Logging Exception Handler" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" logCategory="FileLog" eventId="100" severity="Error" title="Enterprise Library Exception Handling" formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling" priority="0" /> </exceptionHandlers> </add> </exceptionTypes> </add> </exceptionPolicies> </exceptionHandling> <loggingConfiguration name="" tracingEnabled="true" defaultCategory="ConsoleLog"> <listeners> <add listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.CustomTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" type="EFWCoreLib.CoreFrame.EntLib.Log.TraceListeners.ConsoleTraceListener, EFWCoreLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" traceOutputOptions="None" name="ConsoleTraceListener" /> <add listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.CustomTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" type="EFWCoreLib.CoreFrame.EntLib.Log.TraceListeners.FileTraceListener, EFWCoreLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="FileTraceListener" /> <add listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.CustomTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" type="EFWCoreLib.CoreFrame.EntLib.Log.TraceListeners.DatabaseTraceListener, EFWCoreLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="DatabaseTraceListener" /> <add name="Email Trace Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.EmailTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.EmailTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" toAddress="to@example.com" fromAddress="from@example.com" filter="Off" /> <add name="Event Log Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FormattedEventLogTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FormattedEventLogTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" source="Enterprise Library Logging" formatter="Text Formatter" log="" machineName="." traceOutputOptions="None" filter="Error" /> <add name="Flat File Trace Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" fileName="trace.log" formatter="Text Formatter" /> </listeners> <formatters> <add type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" template="Timestamp: {timestamp}{newline}
Message: {message}{newline}
Category: {category}{newline}
Priority: {priority}{newline}
EventId: {eventid}{newline}
Severity: {severity}{newline}
Title:{title}{newline}
Machine: {localMachine}{newline}
App Domain: {localAppDomain}{newline}
ProcessId: {localProcessId}{newline}
Process Name: {localProcessName}{newline}
Thread Name: {threadName}{newline}
Win32 ThreadId:{win32ThreadId}{newline}
Extended Properties: {dictionary({key} - {value}{newline})}" name="Text Formatter" /> <add type="EFWCoreLib.CoreFrame.EntLib.Log.Formatters.ZhyTextFormatter, EFWCoreLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="ZhyTextFormatter" /> </formatters> <categorySources> <add switchValue="All" name="ConsoleLog"> <listeners> <add name="ConsoleTraceListener" /> </listeners> </add> <add switchValue="All" name="FileLog"> <listeners> <add name="Flat File Trace Listener" /> </listeners> </add> <add switchValue="All" name="DatabaseLog"> <listeners> <add name="DatabaseTraceListener" /> </listeners> </add> <add switchValue="All" name="EmailLog"> <listeners> <add name="Email Trace Listener" /> </listeners> </add> </categorySources> <specialSources> <allEvents switchValue="All" name="All Events" /> <notProcessed switchValue="All" name="Unprocessed Category" /> <errors switchValue="All" name="Logging Errors & Warnings"> <listeners> <add name="Event Log Listener" /> </listeners> </errors> </specialSources> </loggingConfiguration> <cachingConfiguration defaultCacheManager="Cache Manager"> <cacheManagers> <add name="Cache Manager" type="Microsoft.Practices.EnterpriseLibrary.Caching.CacheManager, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" expirationPollFrequencyInSeconds="60" maximumElementsInCacheBeforeScavenging="1000" numberToRemoveWhenScavenging="10" backingStoreName="NullBackingStore" /> </cacheManagers> <backingStores> <add type="Microsoft.Practices.EnterpriseLibrary.Caching.BackingStoreImplementations.NullBackingStore, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" name="NullBackingStore" /> </backingStores> </cachingConfiguration> </configuration>View Code
Entlib.config包含了四个节点exceptionHandling、dataConfiguration、loggingConfiguration和cachingConfiguration,分别是对异常的配置、数据库的配置、日志的配置和缓存的配置;其实EntLib.Config配置文件的内容可以集成在Web.config和App.Config的配置文件中,就不需要配置filePath路径,但是分成独立的文件维护起来肯定更方便;
这里有个问题,就是那个filePath指定路径,Web项目一定要指定绝对路径,而Winform项目指定相对路径又可以,绝对路径太麻烦了代码换个地方又要修改此路径;有人解决过的请告诉我一声;
除了上面两处配置,还有一个配置文件EFWUnity.config,涉及到依赖注入的对象需要在此文件中添加配置;
<configuration> <configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration, Version=2.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/> </configSections> <unity> <assembly name="Books"/> <namespace name="Books.Dao"/> <container> <register type="IBookDao" mapTo="SqlBookDao"></register> </container> </unity> </configuration>
其中EntLib配置文件除了手工修改,企业库还提供了可视化工具进行配置,基本上在项目中这些配置都不需要修改;如果是Winform版针对数据库连接字符串需要加密的话,可能需要工具配置一下,EntLib工具提供了加密的功能;
二、对EntLib进行封装
为了使调用EntLib的更方便,还有就是以后EntLib升级的话不需要修改太多地方,所以对EntiLib进一步封装;
所有EntLib对象创建都通过ZhyContainer对象
/// <summary> /// 封装企业库容器 /// </summary> public class ZhyContainer { public static IUnityContainer container = null; /// <summary> /// 获取依赖注入容器 /// </summary> /// <returns></returns> public static IUnityContainer CreateUnity() { container = new UnityContainer(); return container; } public static void AddUnity(UnityConfigurationSection section) { if (container == null) CreateUnity(); container.LoadConfiguration(section); } /// <summary> /// 获取数据库对象 /// </summary> /// <returns>数据库对象</returns> public static Database CreateDataBase() { return EnterpriseLibraryContainer.Current.GetInstance<Database>(); } /// <summary> /// 获取数据库对象 /// </summary> /// <param name="name">数据库实例名(默认name为空,调用默认数据库实例)</param> /// <returns>数据库对象</returns> public static Database CreateDataBase(string name) { return EnterpriseLibraryContainer.Current.GetInstance<Database>(name); } /// <summary> /// 获取日志写入对象 /// </summary> /// <returns></returns> public static LogWriter CreateLog() { return EnterpriseLibraryContainer.Current.GetInstance<LogWriter>(); } public static LogWriter CreateLog(string name) { return EnterpriseLibraryContainer.Current.GetInstance<LogWriter>(name); } /// <summary> /// 获取日志跟踪对象 /// </summary> /// <returns></returns> public static TraceManager CreateTrace() { return EnterpriseLibraryContainer.Current.GetInstance<TraceManager>(); } /// <summary> /// 获取异常处理对象 /// </summary> /// <returns></returns> public static ExceptionManager CreateException() { return EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>(); } public static ICacheManager cacheManager = null; /// <summary> /// 获取缓存对象 /// </summary> /// <returns></returns> public static ICacheManager CreateCache() { cacheManager = CacheFactory.GetCacheManager(); return cacheManager; } public static ICacheManager CreateCache(string name) { cacheManager = EnterpriseLibraryContainer.Current.GetInstance<ICacheManager>(name); return cacheManager; } }View Code
针对EntLib缓存对象ICacheManager的操作封装成CacheHelper对象
/// <summary> /// 缓存操作类 /// </summary> public static class CacheHelper { private static ICacheManager cache = ZhyContainer.CreateCache(); /// <summary> /// 添加缓存 /// </summary> /// <param name="key">键</param> /// <param name="value">值</param> public static void Add(string key, object value) { cache.Add(key, value); } /// <summary> /// 添加缓存 /// </summary> /// <param name="key">键</param> /// <param name="value">值</param> /// <param name="isRefresh">是否刷新</param> public static void Add(string key, object value, bool isRefresh) { if (isRefresh) { //自定义刷新方式,如果过期将自动重新加载,过期时间为5分钟 cache.Add(key, value, CacheItemPriority.Normal, new MyCacheItemRefreshAction(), new AbsoluteTime(TimeSpan.FromMinutes(5))); } else { cache.Add(key, value); } } /// <summary> /// 缓存是否存在此数据 /// </summary> /// <param name="key">键</param> /// <returns></returns> public static bool Contains(string key) { return cache.Contains(key); } /// <summary> /// 获取缓存对象 /// </summary> /// <param name="key">键</param> /// <returns></returns> public static object GetCache(string key) { return cache.GetData(key); } /// <summary> /// 移除缓存对象 /// </summary> /// <param name="key">键</param> public static void RemoveCache(string key) { cache.Remove(key); } }View Code
针对EntLib日志跟踪记录操作封装成LogHelper对象
/// <summary> /// 日志操作类 /// </summary> public static class LogHelper { private static LogWriter logw = ZhyContainer.CreateLog(); private static TraceManager traceMgr = ZhyContainer.CreateTrace(); private static LogEntry loge = new LogEntry(); /// <summary> /// 开始跟踪 /// </summary> /// <returns></returns> public static Tracer StartTrace() { return traceMgr.StartTrace(Category.FileLog); } /// <summary> /// 结束跟踪 /// </summary> /// <param name="trace"></param> public static void EndTrace(Tracer trace) { trace.Dispose(); } }View Code
三、EntLib实现数据库访问
使用EntLib对数据库的访问非常简单,先创建一个数据库对象Database
public SqlServerDb(string key) : base() { database = ZhyContainer.CreateDataBase(key); _connString = database.ConnectionString; }
利用Database对象执行SQL语句
public override int DoCommand(string commandtext) { if (isInTransaction) { command = database.GetSqlStringCommand(commandtext); command.Connection = connection; command.Transaction = transaction; command.CommandType = CommandType.Text; return database.ExecuteNonQuery(command,transaction); } else { return database.ExecuteNonQuery(CommandType.Text, commandtext); } }
当初让我使用EntLib的最重要的原因就是它支持多数据库访问与数据库连接池。数据库连接是有限资源,它们的妥善管理对可扩展的应用程序来说是非常重要的,传统的DbHelper都是在使用完后必须关闭和释放资源,而使用企业库中的Database对象操作数据库我们不用自己处理,企业库会自动处理。
EntLib数据访问程序块资料参考:http://www.cnblogs.com/shanyou/archive/2008/05/25/1206898.html
四、EntLib实现错误日志记录
程序中记录日志是很重要的,好的日志记录方式可以提供我们足够多定位问题的依据;如果只是单独需要日志功能个人推荐Log4net,使用起来更简单;
先看记录的日志文件内容,日志文件是放在根目录的trace.log文件中
五、EntLib实现缓存管理
在构建企业级分布式应用程序时,架构师和开发人员面临着许多难题。缓存可以帮助您克服其中的一些难题,包括:
性能:通过存储与数据使用者尽可能接近的相关数据,缓存可以提高应用程序的性能。这样可以避免重复进行数据创建、处理和传输。
可伸缩性:在缓存中存储信息有助于节省资源,并且可以随着应用程序需求的增加来提高可伸缩性
可用性:通过将数据存储在本地缓存中,应用程序可以承受系统的故障,例如网络等待时间、Web 服务问题以及硬件故障
其实我使用缓存想法很简单,就是全局访问的数据我都存入缓存中,不用缓存也能解决那你得定义很多静态公共变量,但这个代码实现起来比较麻烦;
1)框架中ORM模块对实体的自定义标签配置用到了缓存
cache.Add("entityAttributeList", entityAttributeList);
2)控制器中对配置的内容也用到了缓存
cache.Add("cmdWebController", cmdControllerList);
六、EntLib实现AOP与对象创建
框架中基于EntLib的AOP实现比较简单,先看如何使用AOP:
上面是数据库事务处理利用AOP拦截方法实现;
1)定义自定义标签AOPAttribute
/// <summary> /// AOP标签 /// </summary> [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property |AttributeTargets.Interface)] public class AOPAttribute : HandlerAttribute { private List<Type> _types; /// <summary> /// 创建AOPAttribute实例 /// </summary> /// <param name="types">AOP操作类类型数组</param> public AOPAttribute(params Type[] types) { _types = types.ToList(); } /// <summary> /// 创建AOP管理对象 /// </summary> /// <param name="container">示例容器</param> /// <returns></returns> public override ICallHandler CreateHandler(Microsoft.Practices.Unity.IUnityContainer container) { if (_types.Count > 0) { return new AopCallHandler(_types); } return null; } }View Code
2)AOP操作接口,包括前处理和后处理
/// <summary> /// IAopOperator AOPcaozuofu.html" target="_blank">操作符接口,包括前处理和后处理 /// </summary> public interface IAopOperator { /// <summary> /// 前处理 /// </summary> /// <param name="input"></param> void PreProcess(IMethodInvocation input); /// <summary> /// 后处理 /// </summary> /// <param name="input"></param> /// <param name="result"></param> void PostProcess(IMethodInvocation input, IMethodReturn result); }
3)AOP调用管理类
/// <summary> /// AOP调用管理类 /// </summary> public class AopCallHandler : ICallHandler { private List<IAopOperator> _list; //AOP操作对象列表 /// <summary> /// 创建AopCallHandler实例 /// </summary> /// <param name="list">AOP操作类类型列表</param> public AopCallHandler(List<Type> list) { _list = new List<IAopOperator>(); for (int i = 0; i < list.Count; i++) { _list.Add((IAopOperator)Activator.CreateInstance(list[i])); } } #region ICallHandler 成员 /// <summary> /// 调用执行 /// </summary> /// <param name="input"></param> /// <param name="getNext"></param> /// <returns></returns> public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { IMethodReturn result; for (int i = 0; i < _list.Count; i++) { _list[i].PreProcess(input); } //log result = getNext()(input, getNext); //if (result.Exception == null) //{ // Logger.Write("Action Done."); for (int i = _list.Count - 1; i >= 0; i--) { _list[i].PostProcess(input, result); } //} //log return result; } /// <summary> /// 执行顺序 /// </summary> public int Order { get; set; } #endregion }View Code
4)最后创建一个AOP事务处理类,继承接口IAopOperator功能在前处理和后处理两个方法中实现
public class AopTransaction : IAopOperator { public AopTransaction() { } #region IAopOperator 成员 public void PreProcess(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation input) { List<AbstractDatabase> _RdbList = ((IbindDb)input.Target).GetMoreDb(); AbstractDatabase Rdb = ((IbindDb)input.Target).GetDb(); if (_RdbList == null) { Rdb.BeginTransaction(); } else { foreach (AbstractDatabase db in _RdbList) { db.BeginTransaction(); } } } public void PostProcess(Microsoft.Practices.Unity.InterceptionExtension.IMethodInvocation input, Microsoft.Practices.Unity.InterceptionExtension.IMethodReturn result) { List<AbstractDatabase> _RdbList = ((IbindDb)input.Target).GetMoreDb(); AbstractDatabase Rdb = ((IbindDb)input.Target).GetDb(); if (_RdbList == null) { if (result.Exception == null) { Rdb.CommitTransaction(); } else { Rdb.RollbackTransaction(); } } else { List<AbstractDatabase> RdbList = new List<AbstractDatabase>(); foreach (AbstractDatabase db in _RdbList) { RdbList.Add(db); } RdbList.Reverse();//反序 if (result.Exception == null) { foreach (AbstractDatabase db in RdbList) { db.CommitTransaction(); } } else { foreach (AbstractDatabase db in RdbList) { db.RollbackTransaction(); } } } } #endregion }View Code
EntLib实现AOP学习参考:http://www.cnblogs.com/artech/archive/2008/11/27/1342309.html
利用EntLib的Unity创建逻辑层对象,比如Dao对象、ObjectModel对象,这些对象都不能用new关键字来创建;
Book book = NewObject<Book>();
public T NewObject<T>() { T t = FactoryModel.GetObject<T>(_oleDb,_container, null); return t; }
public static T GetObject<T>(AbstractDatabase Db,IUnityContainer _container, string unityname) { if (Db == null) { EFWCoreLib.CoreFrame.DbProvider.AbstractDatabase Rdb = EFWCoreLib.CoreFrame.DbProvider.FactoryDatabase.GetDatabase(); //SysLoginRight currLoginUser = (SysLoginRight)EFWCoreLib.CoreFrame.Init.AppGlobal.cache.GetData("RoleUser"); //Rdb.WorkId = currLoginUser.WorkId; Db = Rdb; _container = EFWCoreLib.CoreFrame.Init.AppGlobal.container; } //读unity配置文件把类注入到接口得到对象实例 IUnityContainer container = _container; T t = default(T); if (unityname == null) t = container.Resolve<T>(); else t = container.Resolve<T>(unityname); IbindDb ibind = (IbindDb)t; ibind.BindDb(Db,container); //给对象加上代理 t = (T)PolicyInjection.Wrap(t.GetType(), t); return t; }
如果觉得此框架对你有所帮助,请关注我的博客,后续文章会持续更新!