在上一篇文章中,我介绍了企业库Cryptographer模块的一些重要类,同时介绍了企业库Cryptographer模块为我们提供的扩展接口,今天我就要根据这些接口来进行扩展开发,实现2个加密解密方法(离散加密和对称性加密),分别实现自接口IHashProvider和接口ISymmetricCryptoProvider。 首先来看下离散加密——CustomHashCryptography,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
using System; using System.Collections.Generic; //构造函数中接受参数的类型NameValueCollection所在命名空间 using System.Collections.Specialized; using System.Linq; using System.Text; using System.Security.Cryptography; using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;//用于企业库配置工具绑定 using Microsoft.Practices.EnterpriseLibrary.Security.Cryptography; using Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.Configuration; namespace EntLibStudy.Helper { [ConfigurationElementType(typeof(CustomHashProviderData))] public class CustomHashCryptography : IHashProvider { /// <summary> /// 构造函数,此处不可省略,否则会导致异常 /// </summary> /// <param name="attributes">配置文件中所配置的参数</param> public CustomHashCryptography(NameValueCollection attributes) { } /// <summary> /// 比较数据和已加密数据是否相等 /// </summary> /// <param name="plaintext">未加密数据</param> /// <param name="hashedtext">已加密数据</param> /// <returns>是否相等</returns> public bool CompareHash(byte[] plaintext, byte[] hashedtext) { var tmpHashText = CreateHash(plaintext); if (tmpHashText == null || hashedtext == null) return false; if (tmpHashText.Length != hashedtext.Length) return false; for (int i = 0; i < tmpHashText.Length; i++) { if (tmpHashText[i] != hashedtext[i]) return false; } return true; } /// <summary> /// 创建加密 /// </summary> /// <param name="plaintext">待加密数据</param> /// <returns>加密后数据</returns> public byte[] CreateHash(byte[] plaintext) { MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider(); return md5.ComputeHash(plaintext); } } } |
这段代码主要就是实现一个离散加密,不过还是有几点需要注意: 1、在实现接口IHashProvider的基础上,为了能让这个自定义加密可以在企业库的配置工具里调用到需要为类加上一个特性:[ConfigurationElementType(typeof(CustomHashProviderData))],这个特性所在的命名空间为:using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;。 2、这个自定义加密必须包含一个构造函数,其参数的类型是NameValueCollection,这个参数是从配置文件中获取指定的配置属性,见下图: 注意:这个NameValueCollection类型,需要引用命名空间:using System.Collections.Specialized; 如果没有这个构造函数,将会引发异常: Type does not provide a constructor taking a single parameter type of NameValueCollection 3、方法CompareHash、CreateHash,接收和返回的类型都是字节数组。 接下来看下对称加密CustomSymmetricCryptography ,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Text; using System.Security.Cryptography; using System.IO; using Microsoft.Practices.EnterpriseLibrary.Common.Configuration; using Microsoft.Practices.EnterpriseLibrary.Security.Cryptography; using Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.Configuration; namespace EntLibStudy.Helper { [ConfigurationElementType(typeof(CustomSymmetricCryptoProviderData))] public class CustomSymmetricCryptography : ISymmetricCryptoProvider { private string encryptKey=""; public CustomSymmetricCryptography(NameValueCollection attributes) { //从配置文件中获取key,如不存在则指定默认key encryptKey = String.IsNullOrEmpty(attributes["key"]) ? "kyo-yo" : attributes["key"]; } //默认密钥向量 private static byte[] Keys = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF }; /// <summary> /// 加密 /// </summary> /// <param name="ciphertext">待加密数据</param> /// <returns>加密后数据</returns> public byte[] Decrypt(byte[] ciphertext) { if (encryptKey.Length > 8) { encryptKey = encryptKey.Substring(0, 7); } encryptKey = encryptKey.PadRight(8, ' '); byte[] rgbKey = Encoding.UTF8.GetBytes(encryptKey); byte[] rgbIV = Keys; byte[] inputByteArray = ciphertext; DESCryptoServiceProvider DCSP = new DESCryptoServiceProvider(); MemoryStream mStream = new MemoryStream(); CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write); cStream.Write(inputByteArray, 0, inputByteArray.Length); cStream.FlushFinalBlock(); return mStream.ToArray(); } /// <summary> /// 解密 /// </summary> /// <param name="plaintext">加密数据</param> /// <returns>解密后数据</returns> public byte[] Encrypt(byte[] plaintext) { if (encryptKey.Length > 8) { encryptKey = encryptKey.Substring(0, 7); } encryptKey = encryptKey.PadRight(8, ' '); byte[] rgbKey = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 8)); byte[] rgbIV = Keys; byte[] inputByteArray = plaintext; DESCryptoServiceProvider dCSP = new DESCryptoServiceProvider(); MemoryStream mStream = new MemoryStream(); CryptoStream cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write); cStream.Write(inputByteArray, 0, inputByteArray.Length); cStream.FlushFinalBlock(); return mStream.ToArray(); } } } |
这个对称性加密的注意点基本和离散加密一样,但是这边的对称加密我引入了一个加密key,这个key是从配置文件中获取的。 第三点:在项目中应用自定义接口 在上面已经扩展好了2个加密方式,现在就要在实际的项目中运用这2个加密方式,首先打开企业库的配置工具,添加Cryptographer模块,然后在Hash Providers和ISymmetric Cryptograhpy Providers下分别添加刚才定义好的2个加密方式。 注意:添加的自定义加密方式必须放在项目的根目录下,如果放在项目下的文件夹下,如:Helper\Extension下,从企业库的配置文件中将无法找到自定义的加密方式,见下图: 在添加完配置后就可以在web.config看到以下配置信息:
1 2 3 4 5 6 7 8 9 10 |
<securityCryptographyConfiguration> <hashProviders> <add type="EntLibStudy.Helper.CustomHashCryptography, EntLibStudy.Helper, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="CustomHashCryptography" /> </hashProviders> <symmetricCryptoProviders> <add key="kyo-yo" type="EntLibStudy.Helper.CustomSymmetricCryptography, EntLibStudy.Helper, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" name="CustomSymmetricCryptography" /> </symmetricCryptoProviders> </securityCryptographyConfiguration> |
配置完后,我又在Helper.Utils类中添加了几个加密解密方法的封装,用于表示层调用(主要是根据配置实例名和待加密数据获取加密数据),代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
/// <summary> /// 根据配置进行加密 /// </summary> /// <param name="instance">配置实例名</param> /// <param name="encryptString">待加密字符串</param> /// <returns>加密后字符串</returns> public static string Encode(string instance, string encryptString) { return Cryptographer.EncryptSymmetric(instance, encryptString); } /// <summary> /// 根据配置进行解密 /// </summary> /// <param name="instance">配置实例名</param> /// <param name="decryptString">待解密字符串</param> /// <returns>解密后字符串</returns> public static string Decode(string instance, string decryptString) { return Cryptographer.DecryptSymmetric(instance, decryptString); } /// <summary> /// 根据配置进行离散加密 /// </summary> /// <param name="instance">配置实例名</param> /// <param name="plaintString">待加密字符串</param> /// <returns>解密后字符串</returns> public static string CreateHash(string instance, string plaintString) { return Cryptographer.CreateHash(instance, plaintString); } /// <summary> /// 比较离散值是否相等 /// </summary> /// <param name="instance">配置实例名</param> /// <param name="plaintString">未加密字符串</param> /// <param name="hashedString">已加密字符串</param> /// <returns>是否相等</returns> public static bool CompareHash(string instance,string plaintString, string hashedString) { return Cryptographer.CompareHash(instance, plaintString, hashedString); } |
接下来就是主要的项目应用了,在以前的代码中,例如学员的密码我是以明文的形式保存进数据库的,这显示是很不安全的,现在我就要替换这块代码,通过调用Utils.CreateHash方法加密录入的密码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/// <summary> /// 获取已验证的学员对象 /// </summary> /// <param name="student">学员对象</param> /// <returns>是否验证成功</returns> private bool GetValidatedStudent(ref Model.Student student) { if (student == null) { student = new Model.Student(); } student.ClassId = Convert.ToInt32(ddlClass.SelectedValue); student.Sid = txtSid.Text.Trim(); student.Password = Helper.Utils.CreateHash("CustomHashCryptography", txtPwd.Text.Trim()); student.Name = txtName.Text.Trim(); student.Sex = Convert.ToInt32(rblSex.SelectedValue); student.Birthday = DateTime.Parse(txtBirthday.Text.Trim()); return student.IsValid(); } |
以上就是本文的主要内容,本文主要介绍了: 1、如何通过企业库Cryptographer模块给出的接口进行扩展加密方法,以及扩展时需要注意的问题 2、在项目中使用已经扩展好的加密方法。 本文内容比较简单,如发现问题欢迎指出,谢谢大家! 源代码下载:点我下载 注意: 1、MSSQL数据库在DataBase目录下(需要自行附加数据库),SQLite数据库在Web目录的App_Data下,由于考虑到项目的大小,所以每个项目的BIN目录都已经删除,如出现无法生成项目请自行添加相关企业库的DLL。 2、由于微软企业库5.0 学习之路这个系列我是准备以一个小型项目的形式介绍企业库的各模块,所以源代码会根据系列文章的更新而更新,所以源代码不能保证与文章中所贴代码相同。 3、项目开发环境为:VS2010+SQL2005。 4、管理员帐户:admin 密码:admin 转自:http://www.cnblogs.com/kyo-yo/archive/2010/08/11/Learning-EntLib-Seventh-Introduce-Cryptographer-and-Expand-Part2.html
View Details在完成了后,今天开始介绍企业库中的新模块:Cryptographer(加密模块),这个模块在日常的大多数项目的作用非常重要,例如:网站会员密码、身份证号、网站配置等,通过对信息进行加密可以保证项目数据的安全性。 今天主要介绍以下几点: 1、企业库Cryptographer(加密模块)简单分析。 2、实现一个自定义加密接口。 3、在项目中应用自定义接口。 第一点、企业库Cryptographer(加密模块)简单分析 在我们日常开发中总会使用加密对数据进行加密,我们一般都会在项目中自定义一些加密方法,而企业库就是为了简便这些开发,提供了简便的方式来进行数据加密解密。 和其他的模块一样,Cryptographer(加密模块)也可以通过简单的配置进行数据加密解密,具体有关配置方面的介绍可以看园子里huangcong所写的文章,我就不多介绍了: Cryptography Application Block (初级) Cryptography Application Block (高级) Cryptographer(加密模块)主要提供2种加密方式: 1、HashCryptographer(离散加密),这种方法根据特定的算法对数据进行加密,此种加密无法被解密。 2、SymmetricCryptographer(对称性加密),这种方法也是根据特定的算法对数据进行加密,但是数据加密后可以进行解密。 通过图可以更好的理解,下图引用自企业库5.0文档: 加密模块中静态类Cryptographer为核心,方便程序员根据配置对数据进行加密解密,其主要包含以下成员: 1、方法CreateHash,根据配置文件中所配置的离散配置名读取配置对数据进行加密。 2、方法EncryptSymmetric,根据配置文件中所配置的对称性配置名读取配置对数据进行加密。 3、方法DecryptSymmetric,根据配置文件中所配置的对称性配置名读取配置对数据进行解密。 4、方法CompareHash,比较带加密的数据和已加密的数据是否一致。 5、私有方法GetHashProvider、GetSymmetricCryptoProvider,根据配置文件名获取离散加密、对称加密实例。 在这个加密模块中,静态类Cryptographer为我们提供了根据配置名进行加密解密方式,同时还提供了一个抽象类CryptographyManager来实现自定义的加密解密管理器,在加密模块中已经为我们提供了一个CryptographyManager的实现——CryptographyManagerImpl。 抽象类CryptographyManager本质上就是一个非静态的Cryptography,其定义了4个抽象方法: 1、方法CreateHash。 2、方法EncryptSymmetric。 3、方法DecryptSymmetric。 4、方法CompareHash。 实现类CryptographyManagerImpl则主要实现了抽象类CryptographyManager,其主要信息如下: 1、字段IDictionary<string, IHashProvider> hashProviders,离散加密键值对集合,包含了多个离散加密实现。 2、字段IDictionary<string, ISymmetricCryptoProvider> symmetricCryptoProviders,对称加密键值对集合,包含了多个对称加密实现。 3、字段IDefaultCryptographyInstrumentationProvider instrumentationProvider,提供加密模块出现错误报告 4、构造函数,一共有3个构造函数,接收泛型列表形式的离散加密实现、对称加密实现以及 5、加密解密方法CreateHash、EncryptSymmetric、DecryptSymmetric,这3个方法接收3个参数:所配置的加密解密实例名、待加密数据和加密模块的错误报告(IDefaultCryptographyInstrumentationProvider)。 其内部会根据实例名到hashProviders或symmetricCryptoProviders这2个列表中寻找相应的加密解密实现,然后调用实现进行加密解密。 在实际的项目开发的开发过程中,我们既可以通过静态类Cryptographer简单的完成对数据的加密解密,也可以通过实现抽象类CryptographyManager来编写符合项目需求的加密解密管理器,总的来说企业库的这个Cryptographer模块已经为我们提供了很好的加密解密封装,同时又提供了扩展接口在其基础上可以进行进一步扩展,大大方便了我们的日常项目开发。 第二点:实现一个自定义加密接口 在第一点中,我简单介绍了企业库Cryptographer模块的重要信息,现在我来介绍下企业库Cryptographer模块为我们提供的加密解密扩展接口。 上面说过,企业库Cryptographer模块提供了2种加密解密方式: 1、HashCryptographer(离散加密),这种方法根据特定的算法对数据进行加密,此种加密无法被解密。 2、SymmetricCryptographer(对称性加密),这种方法也是根据特定的算法对数据进行加密,但是数据加密后可以进行解密。 这2种加密解密方式对应着2个接口:IHashProvider和ISymmetricCryptoProvider,分别看下这2个接口的代码: IHashProvider接口:
1 2 3 4 5 6 |
public interface IHashProvider { byte[] CreateHash(byte[] plaintext); bool CompareHash(byte[] plaintext, byte[] hashedtext); } |
这个接口只有2个方法: 1、方法CreateHash,接收传入的待加密数据(字节数组)根据具体实现进行离散加密返回加密后的数据(字节数组)。 2、方法CompareHash,接收待加密数据(字节数组)和已经加密后的数据(字节数组),调用具体实现的离散加密方法将待加密数据进行加密然后与已加密数据进行比较,看其是否相等。 ISymmetricCryptoProvider接口:
1 2 3 4 5 6 |
public interface ISymmetricCryptoProvider { byte[] Encrypt(byte[] plaintext); byte[] Decrypt(byte[] ciphertext); } |
这个接口也比较简单,也只有2个方法: 1、方法Encrypt,接收待加密数据(字节数组),调用实现方法进行加密,返回加密后的数据(字节数组)。 2、方法Decrypt,接收已加密数据(字节数组),调用实现方法进行解密,返回解密后的数据(字节数组)。 而如果我们需要扩展自定的加密解密方法据需要从上面2个接口入手,根据需求判断是要进行离散加密还是对称性加密实现不同的接口。 以上就是今天所要介绍的企业库Cryptographer模块信息,主要介绍了Cryptographer模块的常用类、加密解密管理器,同时简单介绍了Cryptographer模块的加密解密扩展接口。 在下一篇文章中我将继续介绍如何实现自定义离散加密和对称性加密方法,以及在项目中使用自定义的加密解密方法。 转自:http://www.cnblogs.com/kyo-yo/archive/2010/08/09/Learning-EntLib-Seventh-Introduce-Cryptographer-and-Expand.html
View Details前端时间花了1个多星期的时间写了使用jQuery.Validate进行客户端验证,但是那仅仅是客户端的验证,在开发项目的过程中,客户端的信息永远是不可信的,所以我们还需要在服务器端进行服务器端的验证已保证数据的正确,今天我继续企业库的学习之路,主要介绍企业库中的Validation模块如何对数据进行验证。 本文的主要内容有以下三点: 1、根据本项目进行实体验证。 2、使用Validation提供的ASP.NET控件将实体验证和UI层页面验证联系起来 3、简单分析下Validation.Integration.Aspnet实现逻辑 文章开始前的废话: 我学习微软企业库都是首先查看企业库提供的HOL(Microsoft Enterprise Library 5.0 – Hands On Labs),里面为企业库的每个模块编写了例子,是非常好的学习材料,而其中的Validation模块的例子是最多的,足足有14个(由此可见Validation模块在企业库中的分量),我看了下,基本上将Validation的各个方面使用方法都介绍了一遍,想学习的朋友可以认真的学习下。 第一点:根据本项目进行实体验证 这个项目是一个小型的学生信息管理系统(就是班级和学生简单管理,都不好意思叫系统),主要有班级、学生、科目3个类,我们现在需要通过企业库的Validation模块为这3个类加上验证。 在前几篇文章中,我已经将企业库的Validation模块的各种验证器基本信息进行了介绍,大家可以前往查看。 我这边就是将student类进行了简单的验证:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
[StringLengthValidator(1, 16, MessageTemplate = "登录ID的长度必须在{3}-{5}之间!")] //MessageTemplateResourceType = typeof(EntLibStudy.Model.Properties.Resources), //MessageTemplateResourceName = "SidMessage")] public string Sid { get; set; } [StringLengthValidator(1, 16, MessageTemplateResourceType = typeof(EntLibStudy.Model.Properties.Resources), MessageTemplateResourceName = "PasswordMessage")] public string Password { get; set; } [StringLengthValidator(1, 16, MessageTemplateResourceType = typeof(EntLibStudy.Model.Properties.Resources), MessageTemplateResourceName = "NameMessage")] public string Name { get; set; } |
这里我就是将Sid、Password和Name进行必须输入验证。 第二点:使用Validation提供的ASP.NET控件将实体验证和UI层页面验证联系起来 在Validation模块中企业库为我们提供了一个子模块——ASP.NET控件用来和Validation模块联合起来进行客户端+服务器端的验证。 我们在页面上放上一个PropertyProxyValidator控件,并指定要验证的控件、对应的实体类型及属性名,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<tr> <td align="right"> 登录ID: </td> <td> <asp:TextBox runat="server" ID="txtSid" /> <cc1:PropertyProxyValidator ID="PropertyProxyValidator1" runat="server" ControlToValidate="txtSid" PropertyName="Sid" SourceTypeName="EntLibStudy.Model.Student,EntlibStudy.Model" ValidationGroup="test"></cc1:PropertyProxyValidator> </td> </tr> <tr> <td align="right"> 密码: </td> <td> <asp:TextBox runat="server" ID="txtPwd" TextMode="Password" /> <cc1:PropertyProxyValidator ID="PropertyProxyValidator2" runat="server" ControlToValidate="txtPwd" PropertyName="Password" SourceTypeName="EntLibStudy.Model.Student,EntlibStudy.Model" ValidationGroup="test"></cc1:PropertyProxyValidator> </td> </tr> <tr> <td align="right"> 姓名: </td> <td> <asp:TextBox runat="server" ID="txtName" /> <cc1:PropertyProxyValidator ID="PropertyProxyValidator3" runat="server" ControlToValidate="txtName" PropertyName="Name" SourceTypeName="EntLibStudy.Model.Student,EntlibStudy.Model" ValidationGroup="test"></cc1:PropertyProxyValidator> </td> </tr> <tr> |
具体有关PropertyProxyValidator控件信息可以看下面的第三点分析,这边仅仅是介绍如何应用。 在页面中添加完指定的控件后,还需要在提交的按钮处做一下处理,代码如下:
1 2 3 4 5 6 7 8 |
protected void btnSubmit_Click(object sender, EventArgs e) { if (!IsValid) { return; } //具体业务逻辑 } |
这步操作是判断页面验证是否通过,如果验证不通过则返回,主要是因为PropertyProxyValidator控件所依赖的验证都在服务器端,需要点击一次提交按钮来进行验证,所以第一次页面的验证是不通过的,需要等到页面上的所有PropertyProxyValidator控件验证通过后页面的验证才会通过。 这时如果未通过验证,服务器端则会通过PropertyProxyValidator控件将验证消息返回给客户端,见下图, 如果全部填写完整则会真正提交页面信息: 虽然企业库的Validation模块为我们提供了这个ASP.NET控件来和Validation进行组合应用,但是我总觉得不友好(最好能使用AJAX验证来提高界面友好性),而且应用面比较窄(可能还有更好的使用方法我没研究出来,如果有哪位朋友有使用经验欢迎分享),使用起来也不怎么方便,建议还是采用ASP.NET原有的验证控件,在数据提交的时候通过后台代码编写进行验证。 我在网上搜索了相关的文章,认为最好的办法就是为Model层的每个子类都统一继承自一个基类,基类里提供统一的验证方法,这样就可以很好的将验证逻辑封装到Model层,表示层只需在获取到数据后调用这个验证方法,验证不通过则将错误消息返回给客户端,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
namespace EntLibStudy.Helper { [Serializable] public abstract class BaseClass<T> where T : class { public string ValidateTag { get; protected set; } public virtual bool IsValid() { var validateResults = Validation.Validate<T>(this as T); if (!validateResults.IsValid) { foreach (var item in validateResults) string.Format(@"{0}:{1}" + Environment.NewLine, item.Key, item.Message); return false; } return true; } } } |
修改抽象类BaseClass,同时添加一个属性用于存储验证结果,添加一个方法用于实体验证。 界面使用代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
protected void btnSubmit_Click(object sender, EventArgs e) { //if (!IsValid) //{ // return; //} BLL.StudentManage studentBll = new BLL.StudentManage(); Model.Student student = null; try { int studentId = Convert.ToInt32(this.ViewState["studentId"]); if (studentId == 0) { if (GetValidatedStudent(ref student)) { int id = studentBll.Add(student); Helper.Utils.MessageBox(this, "新增学员信息成功!", this.GetRouteUrl("StudentRoute" { id = id })); } else Helper.Utils.MessageBox(this, student.ValidateTag); } else { student = studentBll.SelectById(studentId); if (GetValidatedStudent(ref student)) { studentBll.Update(student); Helper.Utils.MessageBox(this, "编辑学员信息成功!"); } else Helper.Utils.MessageBox(this, student.ValidateTag); } } catch (Exception ex) { throw ex; } } /// <summary> /// 获取已验证的学员对象 /// </summary> /// <param name="student">学员对象</param> /// <returns>是否验证成功</returns> private bool GetValidatedStudent(ref Model.Student student) { if (student == null) { student = new Model.Student(); } student.ClassId = Convert.ToInt32(ddlClass.SelectedValue); student.Sid = txtSid.Text.Trim(); student.Password = txtPwd.Text.Trim(); student.Name = txtName.Text.Trim(); student.Sex = Convert.ToInt32(rblSex.SelectedValue); student.Birthday = DateTime.Parse(txtBirthday.Text.Trim()); return student.IsValid(); } |
修改了页面的具体代码,添加一个GetValidatedStudent方法用于统一赋值,同时返回实体对象是否验证通过,如不通过则将验证错误消息返回给客户端 第三点:简单分析下Validation.Integration.Aspnet实现逻辑 在第二点中已经说过了,Validation.Integration.Aspnet是企业库中Validation模块所提供的一个子模块,其本质是一个ASP.NET控件,核心类就是PropertyProxyValidator.cs,下面我来简单的介绍下这个控件。 PropertyProxyValidator控件其主要作用就是根据配置信息去调用指定的服务器端的验证方法进行验证,如验证不通过则返回验证消息。 类PropertyProxyValidator,继承自ASP.NET中的BaseValidator类,同时实现了接口IValidationIntegrationProxy: 1、BaseValidator类为所有验证控件提供核心实现。验证控件用于验证关联的输入控件中的用户输入。当用户输入的值未通过验证时,验证控件将显示错误信息。由于验证控件是与输入控件分开的,您可以将错误信息定位在页面上相对于输入控件的任意位置。ASP.NET 提供了一些验证控件来执行特定类型的验证。(以上摘自MSDN) 由于PropertyProxyValidator继承自BaseValidator类,使得其成为了一个ASP.NET控件(Label控件),基本的客户端验证属性都已经包含在内了。 2、IValidationIntegrationProxy接口是由企业库定义的,其主要作用是为Validation模块提供验证所需行为整合。 在PropertyProxyValidator类中,主要实现了一下方法、属性等: 1、方法BaseValidator.EvaluateIsValid,这个方法的作用是确定输入控件中的值是否有效的代码,其内部实现很简洁,就是创建一个Validator验证器,根据配置通过这个验证器进行验证,然后通过调用内部静态方法FormatErrorMessage来拼装验证返回的消息,最后将这个消息返回给客户端。 PropertyProxyValidator实现的接口IValidationIntegrationProxy主要就为此方法服务,因为创建Validator验证器是通过ValidationIntegrationHelper这个帮助器来创建的,而这个帮助器通过构造函数接收一个实现接口IValidationIntegrationProxy的类,然后帮助器再通过PropertyValidationFactory.GetPropertyValidator属性验证类工厂类中的属性验证器方法调用这个类中所设置好的validatedType(待验证的对象类型)、validatedProperty(待验证的属性信息)、Ruleset(验证规则集)、SpecificationSource(见第5)和ValueAccessBulider(数据访问构造器,这里指向的是类PropertyMappedValidatorValueAccessBuilder)来创建验证器。 2、属性SourceTypeName,待验证对象在服务器端类型,例如:“EntLibStudy.Model.Student,EntLibStudy.Model”,其中逗号前指的是待验证对象所在的类名,逗号后指的是待验证对象所在程序集名。 3、属性PropertyName,待验证对象在服务器端类型中对应的属性名,例如"Name”。 4、属性RulesetName,验证规则集名。 5、属性SpecificationSource,指定的验证信息验证时,调用创建方法所需的源。 6、枚举属性DisplayMode,主要分为3种,List(列表形式)、BulletList(带项目符号的列表形式)和SingleParagraph(段落形式)。 7、事件ValueConvert,用于当控件的值转变时执行验证,对应着委托ValueConverter。 以上就是本文的所有内容了,本文主要介绍了企业库的Validation模块中所提供的ASP.NET验证控件。到此企业库的Validation模块就全部介绍完毕了,谢谢大家的浏览。 PS:有关在BaseClass中抽象验证参考自:將驗證方法封裝在Model之中(一位台湾的朋友写的) 源代码下载:点我下载 转自:http://www.cnblogs.com/kyo-yo/archive/2010/08/04/Learning-EntLib-Sixth-Use-Validation-To-Server-Validate.html
View Details在前一篇文章中,已经将Validation模块的内置的多种验证器(第一类验证器)的使用进行了介绍,今天继续后面两类验证器:独立验证器和自定义验证器,同时对Validation模块下有关验证器的配置类进行遗漏补充。 一、独立验证器 我上篇中我将AndCompositeValidator和OrCompositeValidator归为独立验证器,这2个验证器主要是为了第一类验证服务,可以进行多种验证组合在一起进行复杂验证: AndCompositeValidator——组合验证,逻辑与验证,所包含的多种验证全部通过则通过 OrCompositeValidator——组合验证,逻辑或验证,所包含的多种验证有一个通过则通过 这2类的验证器可以通过配置工具进行配置: 其配置方式和原有的配置一样,只不过是将各个具体的验证器放到了AndCompositeValidator或OrCompositeValidator中了。 当然也可以通过编写代码的方法进行创建:
1 2 3 4 |
Validator v = new AndCompositeValidator( new NotNullValidator(), new StringLengthValidator(1, 16)); v.Validate(Name4); |
这样就将2个验证器统一包装到一起对Name4属性进行验证了。 (注:在第一类的所有验证器由于都是继承自Validator ,所以除了通过特性、配置也可以进行代码编写进行验证) 二、自定义验证 当在进行项目开发时候发现Validation模块所内置的验证器不足以满足我们日常的需要的时候,我们就需要根据根据自己的需求进行扩展,建立自定义验证。 在Validation模块下已经为我们提供了扩展接口(具体有关Validation模块的验证的实现层次可以看“上篇”,具体的验证器都是需要继承自Validator类) 而具体需要实现一个自定义接口还是要思考一下步骤: 1、自定义验证器的验证方式 考虑到自定义验证器的验证方式,确定自己的自定义验证器的验证方式,是仅仅要特性验证,或者配置器验证还是2者都要? 2、开始编码 在确定确定了验证方式就可以开始着手编写具体的验证器了,我们来看下要实现不同验证方式的验证器如何编写(我这边就直接引用微软企业库给出的HOL里面的9-11一共3个例子) 在实现各种验证方式之前首先要建立一个具体的验证器,比如StringLengthValidator等,这边由于我参考的是HOL,所以我就直接引用SSNValidator.cs了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
using System; using System.Text.RegularExpressions; using Microsoft.Practices.EnterpriseLibrary.Validation; using Microsoft.Practices.EnterpriseLibrary.Validation.Validators; namespace ValidationHOL.CustomValidators { public class SSNValidator : Validator<string> { public SSNValidator(string tag) : this(tag, false) { } public SSNValidator(string tag, bool ignoreHypens) : base(string.Empty, tag) { this.ignoreHypens = ignoreHypens; } static Regex ssnCaptureRegex = new Regex(@"^(?<area>\d{3})-(?<group>\d{2})-(?<serial>\d{4})$"); static Regex ssnCaptureNoHypensRegex = new Regex(@"^(?<area>\d{3})(?<group>\d{2})(?<serial>\d{4})$"); private bool ignoreHypens; protected override string DefaultMessageTemplate { get { throw new NotImplementedException(); } } protected override void DoValidate( string objectToValidate, object currentTarget, string key, ValidationResults validationResults) { Match match = (ignoreHypens ? ssnCaptureNoHypensRegex : ssnCaptureRegex) .Match(objectToValidate); if (match.Success) { string area = match.Groups["area"].Value; string group = match.Groups["group"].Value; string serial = match.Groups["serial"].Value; if (area == "666" || string.Compare(area, "772", StringComparison.Ordinal) > 0) { LogValidationResult( validationResults, "Invalid area", currentTarget, key); } else if (area == "000" || group == "00" || serial == "0000") { LogValidationResult( validationResults, "SSN elements cannot be all '0'", currentTarget, key); } } else { LogValidationResult( validationResults, this.ignoreHypens ? "Must be 9 digits" : "Must match the pattern '###-##-####'", currentTarget, key); } } } } |
可以看出这个验证器类还是比较简单的,这个类主要是用于验证美国社会安全号的格式正确性,这个类继承自泛型抽象类Validator<string>,实现了以下功能: 1)实现了属性DefaultMessageTemplate,返回一个异常NotImplementedException,这里我们不用去管它。 2)实现了方法DoValidate,这个是重要点,在前面的文章中也提过,这个方法主要就是用来进行具体的验证的,从方法中可以看出其本质就是通过正则表达式对社会安全号进行验证(验证规则我就不介绍了,有兴趣的可以自己搜索),如果验证不通过则通过方法LogValidationResult来记录消息及结果。 3)构造函数,这个验证有2个构造函数,构造函数接收2个参数,一个参数是验证的标签(存放一些验证信息),另一个参数ignoreHypens是表示在进行验证的时候是否忽略连字符。 在实现完了主要的业务逻辑验证器后我们就可以开始根据验证方式进行编写对应的类了: 1)方法验证 这个验证方式的话就不需要再编写任何代码了,直接实例化上面的SSNValidator后调用方法即DoValidate可进行验证。 2)特性验证 这个验证方式就需要编写一个特性类,我们可以参照Validation模块内置的验证器的格式,取名为SSNValidatorAttribute,看下带代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
using System; using Microsoft.Practices.EnterpriseLibrary.Validation; using Microsoft.Practices.EnterpriseLibrary.Validation.Validators; namespace ValidationHOL.CustomValidators { public class SSNValidatorAttribute : ValidatorAttribute { protected override Validator DoCreateValidator(Type targetType) { return new SSNValidator(this.Tag); } } } |
这个特性类也很简单,就是继承抽象特性类ValidatorAttribute,重载实现方法DoCreateValidator创建验证器SSNValidator进行验证。 这样在具体的使用的时候只需:
1 2 |
[SSNValidator] public string SSN { get; set; } |
3)配置器验证 为了更好的、更方便的进行验证器,我们可以让我们自己写验证通过配置器进行配置,这时我们就需要增加一个SSNValidatorData类,这个类是用来进行和企业库的配置工具进行对接的,看下具体的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
using System; using System.Configuration; using Microsoft.Practices.EnterpriseLibrary.Validation; using Microsoft.Practices.EnterpriseLibrary.Validation.Configuration; using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Design; using ValidationHOL.CustomValidators.Properties; namespace ValidationHOL.CustomValidators.Configuration { [ResourceDescription(typeof(Resources), "SSNValidatorDescription")] [ResourceDisplayName(typeof(Resources), "SSNValidatorName")] public class SSNValidatorData : ValueValidatorData { public SSNValidatorData() { } public SSNValidatorData(string name) : base(name, typeof(SSNValidator)) { } [ConfigurationProperty("ignoreHyphens")] [ResourceDescription(typeof(Resources), "IgnoreHyphensDescription")] [ResourceDisplayName(typeof(Resources), "IgnoreHyphensName")] public bool IgnoreHyphens { get { return (bool)this["ignoreHyphens"]; } set { this["ignoreHyphens"] = value; } } protected override Validator DoCreateValidator(Type targetType) { return new SSNValidator(this.Tag, this.IgnoreHyphens); } } } |
这个配置类,主要有以下3个注意点: 1)如果想在企业库的配置工具进行配置的时候增加所配置的验证器的注释时,需要和验证消息一样将注释放入资源文件,同时在类以及对应的属性上增加特性用以获取注释,如:[ResourceDescription(typeof(Resources), "SSNValidatorDescription")],[ResourceDisplayName(typeof(Resources), "SSNValidatorName")] 2)可以在配置器中增加属性用以从企业库配置工具中获取信息。 3)配置类必须直接或者间接继承抽象类ValueValidatorData,同时实现方法DoCreateValidator用以创建验证器实例进行验证。 完成了配置类的编写后我们还需为验证器类加上一个特性用以标识可以进行配置:
1 2 |
[ConfigurationElementType(typeof(SSNValidatorData))] public class SSNValidator |
重新编译后我们就可以通过配置器进行配置了,我们可以在企业库配置工具里添加一个CustomValidator,然后选择我们编写的SSNValidator: 三、遗漏补充:Validation模块下有关验证器的配置类的简单介绍 在上篇我介绍了验证器和特性类的继承层次以及相关属性信息,但是遗漏了验证器在配置方面的相关信息,今天把这方面补充上,首先看下具体的结构图(这里我还是放了2种类型的验证器配置类,一个是类型转换验证器,另一个是字符串长度验证器): 从这张图中我们可以清楚的了解到Validation模块的验证器的配置类的继承层次: IValidatorDescriptor->ValidatorData->ValueValidatorData->具体验证器配置类 接口IValidatorDescriptor 包含一个方法CreateValidator,实现类可以根据需求创建验证,同时方便企业库配置器获取所配置的具体验证类。 抽象类ValidatorData 实现接口IValidatorDescriptor,其作用基本类似于验证特性类BaseValidationAttribute和ValidatorAttribute的结合体,抽象了验证器创建方法、验证消息、验证结果等: 1)属性MessageTemplate,验证消息模板 2)属性MessageTemplateResourceName、MessageTemplateResourceTypeName,消息模板所在资源名和资源类型 3)属性Tag,存放验证结果 4)虚方法DoCreateValidator,用以被子类重写创建具体的验证器 5)方法IValidatorDescriptor.CreateValidator,实现接口IValidatorDescriptor的创建验证器的方法,其内部调用虚方法DoCreateValidator来获取具体的验证器 6)方法GetMessageTemplate、GetMessageTemplateResourceType,获取消息模板和根据资源类型获取消息模板 抽象类ValueValidatorData 封装了一个否定逻辑的属性,其作用基本类似于验证特性类ValueValidatorAttribute,但比ValueValidatorAttribute少了方法IsValid和FormatErrorMessage,功能单一。 具体验证器配置类 这个类就是根据具体的业务逻辑进行创建的,其必须实现方法Validator DoCreateValidator(Type targetType)根据配置信息读取所验证的对象,然后创建具体的验证器进行验证。 抽象泛型类RangeValidatorData<T> 在上图中还有一个类RangeValidatorData<T>,这个类不同于一般业务逻辑验证器配置类,这个类是为所有需要进行范围验证的验证器配置类提供抽象: 1)泛型属性LowerBound、UpperBound,范围上限下限 2)枚举属性LowerBoundType,UpperBoundType,表示范围边界 3)这个类的泛型T必须可以实现IComparable<T> (注:在配置类的所有属性上,如需要添加注释以方便在企业库配置工具中显示需要和验证消息一样将注释放入资源文件,同时在类以及对应的属性上增加特性用以获取注释,如:[ResourceDescription(typeof(Resources), "SSNValidatorDescription")],[ResourceDisplayName(typeof(Resources), "SSNValidatorName")]) 至此企业库Validation模块信息、验证器实现层次以及各种验证器的使用方法就介绍完了,基本都是来自于企业库的源代码以及我个人的理解,文中如有不对请大家指出。 下一篇我将继续企业库Validation模块的介绍,主要是介绍Validation模块在学习之路项目中的一些简单应用,以及企业库Validation模块所提供的Asp.net控件的简单分析。 转自:http://www.cnblogs.com/kyo-yo/archive/2010/08/02/Learning-EntLib-Fifth-Introduction-Validation-module-information-Part3.html
View Details在上一篇文章中,我介绍了企业库的Validation模块的基本信息、使用方法,同时对企业库的内置验证器的实现及验证器的特性类实现进行了简单的分析,今天我将继续前一篇文章,介绍Validation模块内置的多种验证器的使用进行介绍。 (注:本文仅仅就Validation模块的各种验证器根据我个人的理解进行介绍,如果有误欢迎大家指出!) 0、验证器的命名参数 在上一篇文章中我已经介绍过验证器的特性类的层次: BaseValidationAttribute->ValidatorAttribute ->ValueValidatorAttribute ->具体验证器特性类 在具体的验证器的父类们中,已经封装好了许多通用的验证参数: 1)ErrorMessage,继承自ValidationAttribute,在Validation模块中没用使用。 2)ErrorMessageResourceName,继承自ValidationAttribute,在Validation模块中没用使用。 3)ErrorMessageResourceType,继承自ValidationAttribute,在Validation模块中没用使用。 4)MessageTemplate,验证错误消息模板,继承自BaseValidationAttribute。 5)MessageTemplateResourceName,验证错误消息模板所在资源名,继承自BaseValidationAttribute。 6)MessageTemplateResourceType,验证错误消息模板所在资源类型,继承自BaseValidationAttribute。 7)Negated,否定验证逻辑,继承自ValueValidatorAttribute。 8)Ruleset,验证规则集,继承自BaseValidationAttribute。 9)Tag,验证结果,继承自BaseValidationAttribute。 这些参数在每个具体的验证器中都是通用的,都被设置为可选的命名参数,使用者可以根据业务需求指定需要的参数。 例如指定错误消息:MessageTemplate = "名称中必须包含kyo字符串!",具体参数的介绍可以查看上一篇文章。 下面介绍每个验证器的时候就不介绍了。 1、ContainsCharactersValidator——是否包含字符串验证 这个验证器主要是验证字符串对象中是否包含指定的字符串,其具体用法如下: ? 1 2 3 4 [ContainsCharactersValidator("kyo")] [ContainsCharactersValidator("kyo", MessageTemplate = "名称中必须包含kyo字符串!")] [ContainsCharactersValidator("kyo", ContainsCharacters.All)] //ContainsCharacters.Any public string Name { get; set; } 这个验证器有4个重载的构造函数,接收3类参数: 1)字符串characterSet,指定要求包含的字符串 2)枚举ContainsCharacters,包含字符串验证枚举,有2个值:All(待验证的字符串必须包含指定的字符串中所有字符),Any(待验证的字符串只需包含指定的字符串中一个字符) 3)命名参数 2、DateTimeRangeValidator——时间范围验证 这个验证器主要是验证日期对象是否在指定范围内,具体的使用方法如下: ? 1 2 [DateTimeRangeValidator("1990-01-01T00:00:00+08:00", RangeBoundaryType.Exclusive, "2010-01-01T00:00:00+08:00", RangeBoundaryType.Inclusive)] public DateTime Birthday { get; set; } 这个验证器一共有12个构造函数,接收3类参数: 1)日期类型lowerBound、upperBound,字符串类型lowerBound、upperBound,这类参数是接收日期范围的上限和下限,日期类型的lowerBound、upperBound我就不多说了。 需要注意的是字符串类型lowerBound、upperBound,字符串类型的上限下限日期格式必须要符合ISO8601格式,就如我给出的代码一样,具体有关ISO8601知识可以看百度百科:ISO8601,里面有很详细的介绍。 2)枚举RangeBoundaryType,表示日期上限下限的范围,共有3个值:Ignore(忽略边界)、Inclusive(可以等于边界,下限<=值<=上限),Exclusive(不可以等于边界,就是小于边界,下限<值<上限) 3)命名参数 3、DomainValidator——通过检查验证对象,判断它是否属于一个集合 这个验证器主要是验证对象是否在一个指定的集合里,具体的使用方法如下: ? 1 2 [DomainValidator("a1", "a2", "a3")] public string DomainItem { get; set; } 这个验证器只有4个构造函数,接收2类参数: 1)param参数集合domain,指定验证的集合 2)命名参数 4、EnumConversionValidator——检查字符串是否可以转换成枚举中的某一项 这个验证器主要是验证字符串对象是否可以转换成指定的枚举中某一项的值,其具体使用方法如下: ? 1 2 3 4 5 6 7 public enum ValidationEnum { A = 1, B = 2 } [EnumConversionValidator(typeof(ValidationEnum))] public string EnumItem { get; set; } 这个验证很简单,只有2个构造函数,接收2类参数: 1)枚举的类型enumType,指定要验证的枚举类型 2)命名参数 5、HasSelfValidation——自我验证,只能用于类,为SelfValidationAttribute服务 这个验证器比较特殊,它无法通过企业库的配置工具进行配置,它仅仅是为要进行自验证的类添加一个表示,其本身没有什么业务逻辑,同时它也只能作用于类上,其具体用法如下: ? 1 2 [HasSelfValidation] public class TestValidation 由于其本身没有业务逻辑,所以不需要接收任何参数。 […]
View Details本文是为后面的学习之路做铺垫,简单介绍下企业库中的Validation模块的一些相关知识,包括Validation模块的简介、用途、使用方法、默认提供的多种验证器的介绍等。 一、简介及用途 在实际的项目开发中,我们总会需要对数据进行验证,以保证数据的可靠性,而为了使这些验证可以在不同的地方进行复用(如winform、web、WPF等),就需要将验证进行封装,EntLib的Validation模块就为我们解决了这个问题而产生的,其内置了多种常见的验证器,如字符串、日期、类型转换、正则表达式等,同时还提供了自定义验证的接口,方便我们根据实际的业务需求进行扩展。 同时,在Validation模块中还为我们加入了ASPNET、WINFORM和WCF的验证器。 二、使用方法 企业库的Validation模块为我们提供了2种验证方式: 1、在具体的类、属性、字段和方法前添加特性,如:[StringLengthValidator(1, 25)] 2、使用企业库提供的配置工具,通过配置的方式将验证信息存入配置文件 当然你也可以动态的添加验证规则,这个就不是常规使用方式了,这里就不讨论了。 首先介绍下第一种验证方式: 在最新的EntLib5中,Validation模块被进行了修改,各验证Attribute的继承也进行了修改,导致了原有的4.1直接在字段等前面书写Attribute无法使用。 会显示错误信息:StringLengthValidator不是一个有效的AttributeClass 参考园子里:Enterprise Library 5.0 中的Validators 在上面链接的文章里已经需要引用System.ComponentModel.DataAnnotations命名空间,主要原因就是Validation模块的所有验证器都是继承自命名空间System.ComponentModel.DataAnnotations下的ValidationAttribute类(MSDN描述:作为所有验证特性的基类),所以我们如果需要使用特性的方式为实体类或字段等进行验证时必须为项目添加System.ComponentModel.DataAnnotations引用。 接着我们来看下,如何使用特性进行验证: 在代码中引入:
1 2 |
using Microsoft.Practices.EnterpriseLibrary.Validation; using Microsoft.Practices.EnterpriseLibrary.Validation.Validators; |
然后就可以在类、属性、字段和方法前添加特性了,下面是一个简单的字符串长度验证,表示输入的用户名长度必须在1-16之间:
1 2 3 4 5 |
[StringLengthValidator(1, 16, MessageTemplate = "用户名长度必须在1-16位之间")] [StringLengthValidator(1, 16, MessageTemplateResourceType = typeof(EntlibStudy.Model.Properties.Resources), MessageTemplateResourceName = "SIDMessage")] public string Sid { get; set; } |
这边有2点需要注意: 1、我书写了2种验证失败的错误消息,一种是直接书写MessageTemplate,这种方式的好处就是简便(在使用企业库配置工具进行配置时也是设置这个属性),另一种是将错误消息统一放在一个资源文件中,这种的好处是可以将所有的错误消息统一管理,而且如果项目需要进行国际化处理时,可以直接添加新的资源文件即可,坏处是书写起来比较烦,需要指定资源文件的类型及对应消息的资源名。 具体关于StringLengthValidator的使用在下面会讲到。 2、使用特性进行验证时,特性只能应用于public的字段或属性,否则会报错。在3.0以前没有自动属性时我们通常会使用一下方式书写属性:
1 2 3 4 5 6 7 |
[StringLengthValidator(1, 16, MessageTemplate = "用户名长度必须在1-16位之间")] private string _aid; public string Aid { get { return _aid; } set { _aid = value; } } |
很不幸的是,这种方式是错的,因为特性指定的字段是private的,而且就算把特性书写到属性Aid上也是不可行的,因为最终还是为_aid赋值。 接下来应该是介绍如何使用企业库的配置工具进行配置了,不过有关配置的方法园子里已经有很多人写过了,想学习的朋友可以到这边查看: Microsoft Enterprise Library 5.0 系列(三) Validation Application Block (高级) Enterprise Library 4.1 Validation Block 快速使用图文笔记 这2篇都已经将每步的配置进行截图,已经很详细了,我就不多介绍了。 三、Validation模块内置的多种验证器的介绍 在Validation模块中已经内置了很多验证器,这些验证器也有所不同,我自己总结了一下这些验证的分类: 1、可作为特性的验证器,这种验证器是可以直接通过特性为类、属性、字段段进行验证,同时又可以通过配置进行验证。主要有: ContainsCharactersValidatorAttribute——是否包含字符串验证 DateTimeRangeValidatorAttribute——时间范围验证 DomainValidatorAttribute——通过检查验证对象,判断它是否属于一个集合 EnumConversionValidatorAttribute——检查字符串是否可以转换成枚举中的某一项 HasSelfValidationAttribute——自我验证,只能用于类,为SelfValidationAttribute服务 IgnoreNullsAttribute——可为空验证 NotNullValidatorAttribute——不为空验证 ObjectCollectionValidatorAttribute——对象集合类型验证 ObjectValidatorAttribute——对象类型验证 PropertyComparisonValidatorAttribute——属性比较验证 RangeValidatorAttribute——范围验证,判断对象是否在一个指定范围内 RegexValidatorAttribute——正则表达式验证 RelativeDateTimeValidatorAttribute——检查日期是否属于一个范围相对于当前时间或日期 SelfValidationAttribute——对调用一个方法进行自验证 StringLengthValidatorAttribute——字符串长度验证 TypeConversionValidatorAttribute——类型转换验证,判断是否可以转换成指定类型 ValidatorComposition——验证组合器,可以将不同的验证组合在一起 2、独立验证器,主要是为第一种验证服务,可以进行多种验证组合在一起进行复杂验证,如: AndCompositeValidator——组合验证,逻辑与验证,所包含的多种验证全部通过则通过 OrCompositeValidator——组合验证,逻辑或验证,所包含的多种验证有一个通过则通过 3、自定义验证,这种则是为需要根据自己的业务逻辑进行扩展验证所提供的一个扩展。 首先来介绍下第一种验证,可作为特性的验证器: 这类验证工作原理简单的说就是通过特性类来接收要验证的信息,再通过特性类的DoCreateValidator来创建相应的验证类来进行验证。 *具体验证器特性类 *这类验证器的特性最终都是继承自抽象类:BaseValidationAttribute,在这个类的主要作用就是为子类提供错误消息和规则的抽象封装
1 |
public abstract class BaseValidationAttribute : ValidationAttribute |
主要有以下几个字段及方法: 1、ruleset:规则集,开发人员可以为要验证的对象设置规则集,可以在每个规则集下建立针对某个字段或属性等的不同验证方式,例如在A规则集中,对Name字段需要有长度1-16的限制,而在B规则集中则是是否包含“test”字符串,这样通过制定不同的规则集则可以根据实际需求进行不同的验证。 2、messageTemplate,消息模板,指定验证不通过的错误消息 3、messageTemplateResourceType,消息模板所在资源类型,指定消息所存放的资源的类型 4、messageTemplateResourceName,消息模板所在资源名称,指定消息所存放的资源名称 5、tag,验证标签,获取或设置标签所代表的特征验证的结果记录 6、string GetMessageTemplate(),获取消息模板的方法,通过指定的messageTemplateResourceType和messageTemplateResourceName获取错误消息 7、bool IsValid(),重写ValidationAttribute.IsValid方法 在具体的验证特性类和BaseValidationAttribute之间还存在着2个特性类:
1 |
public abstract class ValidatorAttribute : BaseValidationAttribute, IValidatorDescriptor |
*这个类的作用是抽象出验证逻辑以供子类使用: 1、方法CreateValidator,调用DoCreateValidator创建的Validator实例,并将tag和消息模板赋给Validator实例。 2、方法DoCreateValidator,用以创建实现Validator类的特定验证实例,以供各验证器进行验证。
1 |
public abstract class ValueValidatorAttribute : ValidatorAttribute |
*这个类的作用是为各验证器的特性类提供抽象,同时重写了父类方法: 1、方法IsValid,重写了BaseValidationAttribute.IsValid,用以判断对象值是否有效。 2、方法FormatErrorMessage,重写了ValidationAttribute,格式化错误消息 3、属性Negated,用以获取或设置否定逻辑 这样我们就可以清楚的知道每个验证器特性类的具体继承关系了: BaseValidationAttribute->ValidatorAttribute ->ValueValidatorAttribute ->具体验证器特性类,看图可以更加直观一点(为了看的清楚,我这边仅放了2个具体的验证器特性类): 至于具体的验证器特性类则根据不同业务逻辑进行设置,这边就不讲解,想了解的朋友可以或直接看源代码。 *具体验证类 在上面已经说过,具体的验证器验证是通过验证器的特性类(如:StringLengthValidatorAttribute)调用ValidatorAttribute类中的DoCreateValidator方法来创建具体验证器(如:StringLengthValidator)来进行验证。 […]
View Details在前面的企业库学习之路里我分别使用了Data Access构建多数据库访问和使用Exception Handle+Logging记录系统的异常。今天我来介绍下企业库中的Caching模块在本项目中如何应用。 首先先补习下企业库的Caching Application Block的相关知识: 1、四大缓存方式,在Caching Application Block中,主要提供以下四种保存缓存数据的途径,分别是:内存存储(默认)、独立存储(Isolated Storage)、数据库存储(DataBase Cache Storage)和自定义存储(Custom Cache Storage)。 2、多种存储方式,抛开自定义存储不谈,内存存储是最基本的缓存,仅仅是将数据缓存到内存当中,虽然速度快但是无法进行持久化存储,而独立存储和数据库存储一个是存储到本地的磁盘中(视操作系统不同存储到不同的位置)而另一个则是存储到数据库中(方便进行分布式缓存),所以可以进行持久化保存不会因为关机而丢失(可以到。在EntLib50Src\Blocks\Caching\Src\Database\Scripts下找到脚本进行安装) 3、优秀的易用性,虽然在.NET类库System.Web中已经提供了Cache类,但是有局限性,仅可适用于控制台、Winform、Web、服务等。 4、安全性,企业库中的缓存模块可以和加密模块很好的结合起来,当适用数据库缓存、独立存储或者自定义存储的时候可以适用加密模块对缓存的数据进行加密,但存储到内存当中的数据就无法进行加密了。 在了解了缓存的基本知识后我们就可以开始进行具体的操作了。 我现在就是使用Cache模块为项目中反射具体数据库DAL层对象实例进行缓存,这样不用每次在调用底层的时候都反射一次,只需在第1次反射后缓存,以后的访问直接从缓存中读取,提高了访问的速度。 通过企业库配置工具添加个Caching Settings 这里使用默认设置,保存到内存中,过期轮询时间,最大存储数量和移除数量都使用了默认的设置。 如果不想使用默认的内存存储可以建立独立存储或者数据库存储。 这里有个要提的就是企业库的缓存模块的数据库存储是使用存储过程来进行缓存与数据库之间的交互,但是本项目中使用了多数据库,如Sqlite,就无法支持存储过程,所以这边需要自定义存储方式,可以直接查看企业库代码中Cache.DataBase.DataBackingStore.cs类,仿照DataBackingStore类自定义一个存储方式,只不过在进行数据库交互的时候使用SQL语句进行。 继续回到主题上,我这边写了一个简单的CacheHelper,用以操作缓存,其中我自定义了一个缓存刷新操作类(此类必须为可序列化),用于将已经过期的对象重新加入到缓存当中,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Practices.EnterpriseLibrary.Caching; using Microsoft.Practices.EnterpriseLibrary.Caching.Expirations; using Microsoft.Practices.EnterpriseLibrary.Common.Configuration; namespace EntLibStudy.Helper { public static class CacheHelper { //2种建立CacheManager的方式 //ICacheManager cache = EnterpriseLibraryContainer.Current.GetInstance<ICacheManager>(); private static ICacheManager cache = CacheFactory.GetCacheManager(); /// <summary> /// 添加缓存 /// </summary> /// <param name="key">键</param> /// <param name="value">值</param> /// <param name="isRefresh">是否刷新</param> public static void Add(string key, object value, bool isRefresh = false) { 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 object GetCache(string key) { return cache.GetData(key); } /// <summary> /// 移除缓存对象 /// </summary> /// <param name="key">键</param> public static void RemoveCache(string key) { cache.Remove(key); } } /// <summary> /// 自定义缓存刷新操作 /// </summary> [Serializable] public class MyCacheItemRefreshAction : ICacheItemRefreshAction { #region ICacheItemRefreshAction 成员 /// <summary> /// 自定义刷新操作 /// </summary> /// <param name="removedKey">移除的键</param> /// <param name="expiredValue">过期的值</param> /// <param name="removalReason">移除理由</param> void ICacheItemRefreshAction.Refresh(string removedKey, object expiredValue, CacheItemRemovedReason removalReason) { if (removalReason == CacheItemRemovedReason.Expired) { ICacheManager cache = CacheFactory.GetCacheManager(); cache.Add(removedKey, expiredValue); } } #endregion } } |
1、缓存等级,在企业库的缓存模块中已经提供了4个缓存等级:Low,Normal,High和NotRemovable,在超出最大缓存数量后会自动根据缓存等级来移除对象。 2、ICacheItemRefreshAction,这个接口用来方便开发人员扩展使用的,开发人员可以根据移除原因在对象过期后进行相应的操作,其中CacheItemRemovedReason分 Expired:过期被移除 Removed:被手动移除 Scavenged:因为缓存数量已满,则根据缓存等级移除较低级的缓存 Unknown:未知移除,不建议使用 3、过期方式,企业库默认提供4种过期方式 AbsoluteTime:绝对是时间过期,传递一个时间对象指定到时过期 SlidingTime:缓存在最后一次访问之后多少时间后过期,默认为2分钟,有2个构造函数可以指定一个过期时间或指定一个过期时间和一个最后使用时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public SlidingTime(TimeSpan slidingExpiration) { // Check that expiration is a valid numeric value if (!(slidingExpiration.TotalSeconds >= 1)) { throw new ArgumentOutOfRangeException("slidingExpiration", Resources.ExceptionRangeSlidingExpiration); } this.itemSlidingExpiration = slidingExpiration; } public SlidingTime(TimeSpan slidingExpiration, DateTime originalTimeStamp) : this(slidingExpiration) { timeLastUsed = originalTimeStamp; } |
ExtendedFormatTime :指定过期格式,以特定的格式来过期,通过ExtendedFormat.cs类来包装过期方式,具体可参照ExtendedFormat.cs,源代码中已经给出了很多方式 FileDependency:依赖于文件过期,当所依赖的文件被修改则过期,这个我觉得很有用,因为在许多网站,如论坛、新闻系统等都需要大量的配置,可以将配置文件信息进行缓存,将依赖项设为配置文件,这样当用户更改了配置文件后通过ICacheItemRefreshAction.Refresh可以自动重新缓存。 在介绍了Cache的相关参数后我们来看下具体如何使用,我这边将原来的DataAccess类重新修改了一下,因为觉得如果每次多增加一个数据表,对应的工厂就需要多写一个反射方法实在是不方便,所以修改成泛型类(同时附了原来的反射代码,可以对比下那种方式比较好),在BLL层调用的时候只需传递要转成的接口即可,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
public static class DataAccess<T> { private static readonly string assemblyString = ConfigurationManager.AppSettings["DAL"]; /// <summary> /// 通用对象反射(包含缓存) /// </summary> /// <param name="className">要反射的类名</param> /// <returns></returns> public static T CreateObject(string className) { var typeName = assemblyString + "." + className; //判断对象是否被缓存,如果已经缓存则直接从缓存中读取,反之则直接反射并缓存 var obj = (T)CacheHelper.GetCache(typeName); if (obj == null) { obj = (T)Assembly.Load(assemblyString).CreateInstance(typeName, true); CacheHelper.Add(typeName, obj, true); } return obj; } public static IClassInfoService CreateClassInfo() { string typeName = assemblyString + ".ClassInfoService"; //判断对象是否被缓存,如果已经缓存则直接从缓存中读取,反之则直接反射并缓存 if (CacheHelper.GetCache(typeName) != null) { return (IClassInfoService)CacheHelper.GetCache(typeName); } else { IClassInfoService service = (IClassInfoService)Assembly.Load(assemblyString).CreateInstance(typeName, true); CacheHelper.Add(typeName, service, true); return service; } } |
BLL层调用代码如下:
1 |
private IClassInfoService classInfoService = DataAccess<IClassInfoService>.CreateObject("ClassInfoService"); |
需要注意的是由于使用企业库的Cache,如果缓存到数据库或者独立存储必须要求缓存对象必须是可序列化的,内存中缓存就不需要,而我这边缓存的对象为DAL层中具体的操作类,所以如果要更改为非内存存储需要将操作类加上[Serializable]特性。 这样以后再添加新的表就无需修改工厂中的DataAccess类了。 以上就是缓存在本项目中的一些基本应用,由于水平有限,所以暂时无法提出缓存的一些高级应用,请大家见谅。 相关Cache模块配置可以查看huangcong写的Cache模块(初级),一些相关知识可以查看virusswb写的缓存的设计目的。 注意: 1、MSSQL数据库在DataBase目录下(需要自行附加数据库),SQLite数据库在Web目录的App_Data下,由于考虑到项目的大小,所以每个项目的BIN目录都已经删除,如出现无法生成项目请自行添加相关企业库的DLL。 2、由于微软企业库5.0 学习之路这个系列我是准备以一个小型项目的形式介绍企业库的各模块,所以源代码会根据系列文章的更新而更新,所以源代码不能保证与文章中所贴代码相同。 3、项目开发环境为:VS2010+SQL2005。 4、管理员帐户:admin 密码:admin 源代码下载地址:点我下载 转自:http://www.cnblogs.com/kyo-yo/archive/2010/06/24/Learning-EntLib-Forth-Use-Caching.html
View Details在前一篇文章:[EntLib]微软企业库5.0 学习之路——第二步、使用VS2010+Data Access模块建立多数据库项目中我们搭建好了项目的整体多数据库环境,实现了项目的多数据库访问,而整个项目中最主要的异常处理却没有进行部署,今天我们就使用企业库中的Exception Handling+Logging模块为项目加上异常处理以及异常日志记录。 (注:关于Exception Handling和Logging模块的相关基本概念可以查看TerryLee的异常处理和日志检测这2篇文章) 首先说一下企业库Logging模块的个人感觉,个人感觉企业库的日志记录太繁琐了,而且要自定义也比较烦,无法通过简单的配置达到我自己的要求,企业库中的日志记录模块在可以记录许多信息如下: Timestamp: 2010-6-12 3:16:39 Message: HandlingInstanceID: 669fed01-a758-434b-896e-a8e25ebf8c9b An exception of type 'System.Exception' occurred and was caught. —————————————————————- 06/12/2010 11:16:39 Type : System.Exception, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Message : Test Source : EntLibStudy.Helper Help link : Data : System.Collections.ListDictionaryInternal TargetSite : System.String Test() Stack Trace : 在 EntLibStudy.Helper.BasePage.Test() 位置 F:\EntLibStudy\Helper\BasePage.cs:行号 87 在 EntLibStudy.Helper.BasePage.<Page_Load>b__0() 位置 F:\EntLibStudy\Helper\BasePage.cs:行号 81 在 Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.ExceptionManagerImpl.Process[TResult](Func`1 action, TResult defaultResult, String policyName) 位置 以下省略N行。。。。。。 这些信息很多都不是我想要的,我想要的仅仅是异常的提示信息,异常发生的时间,以及异常发生的位置,好方便我们第一时间到异常发生的源头进行调试检查(可能企业库的这些异常信息更加有用,但是我个人认为很多时候都会干扰我们),所以我们仅仅需要其中的几条有用的信息就够了,比如Message,Timestamp、Stack Trace和Severity这4个就基本上够用了,所以我做了个处理,就是使用企业库中Logging模块提供的自定义CustomerTraceListener来实现我们需要的功能。 首先建立一个异常日志记录表(SQLite版)
1 2 3 4 5 6 7 |
CREATE TABLE [ExceptionLog] ( [Id] integer PRIMARY KEY AUTOINCREMENT NOT NULL, [Message] nvarchar(1024) NOT NULL, [LogDate] nvarchar(1024) NOT NULL, [ExceptionLevel] nvarchar(32) NOT NULL, [Exception] ntext NOT NULL ) |
我编写了一个类继承自CustomTraceListener,并重写了记录方法,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Text; using Microsoft.Practices.EnterpriseLibrary.Common.Configuration; using Microsoft.Practices.EnterpriseLibrary.Data; using Microsoft.Practices.EnterpriseLibrary.Logging; using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration; using Microsoft.Practices.EnterpriseLibrary.Logging.Formatters; using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners; namespace EntLibStudy.Helper.EntLibExtension.ExceptionExtension { [ConfigurationElementType(typeof(CustomTraceListenerData))] public class ExceptionCustomerListener : CustomTraceListener { string writeLogSQL = String.Empty; Database database; Exception ex; public ExceptionCustomerListener() : base() { database = DBHelper.CreateDataBase(); } public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data) { if ((this.Filter == null) || this.Filter.ShouldTrace(eventCache, source, eventType, id, null, null, data, null)) { if (data is LogEntry) { LogEntry logEntry = data as LogEntry; ExecuteSQL(logEntry); } else if (data is string) { Write(data as string); } else { base.TraceData(eventCache, source, eventType, id, data); } } } public override void Write(string message) { ExecuteWriteLogSQL(TraceEventType.Information, DateTime.Now, message, database); } public override void WriteLine(string message) { Write(message); } /// <summary> ///执行SQL /// </summary> /// <param name="logEntry">日志对象</param> private void ExecuteSQL(LogEntry logEntry) { using (DbConnection connection = database.CreateConnection()) { try { connection.Open(); using (DbTransaction transaction = connection.BeginTransaction()) { try { ExecuteWriteLogSQL(logEntry, database, transaction); transaction.Commit(); } catch { transaction.Rollback(); throw; } } } finally { connection.Close(); } } } /// <summary> /// 执行写入日志数据库语句 /// </summary> /// <param name="severity">异常等级</param> /// <param name="message">消息</param> /// <param name="db">保存日志的数据库实例</param> private void ExecuteWriteLogSQL(TraceEventType severity, DateTime timeStamp, string message, Database db) { writeLogSQL = (string)this.Attributes["writeLogSQL"]; DbCommand cmd = db.GetSqlStringCommand(writeLogSQL); string exceptionMessage = Utils.GetBetweenString(message, "Message :", "Source :", 9); string exceptionInfo = Utils.GetBetweenString(message, "Stack Trace :", "Additional Info:", 13); db.AddInParameter(cmd, "@Message", DbType.String, exceptionMessage); db.AddInParameter(cmd, "@LogDate", DbType.DateTime, timeStamp); db.AddInParameter(cmd, "@Level", DbType.String, message); db.AddInParameter(cmd, "@Exception", DbType.String, exceptionInfo); db.ExecuteNonQuery(cmd); } /// <summary> /// 执行写入日志数据库语句 /// </summary> /// <param name="logEntry">日志对象</param> /// <param name="db">保存日志的数据库实例</param> /// <param name="transaction">事务对象</param> private void ExecuteWriteLogSQL(LogEntry logEntry, Database db, DbTransaction transaction) { writeLogSQL = (string)this.Attributes["writeLogSQL"]; DbCommand cmd = db.GetSqlStringCommand(writeLogSQL); string exceptionMessage = Utils.GetBetweenString(logEntry.Message, "Message :", "Source :", 9); string exceptionInfo = Utils.GetBetweenString(logEntry.Message, "Stack Trace :", "Additional Info:", 13); db.AddInParameter(cmd, "@Message", DbType.String, exceptionMessage); db.AddInParameter(cmd, "@LogDate", DbType.DateTime, logEntry.TimeStamp.ToLocalTime()); db.AddInParameter(cmd, "@Level", DbType.String, logEntry.LoggedSeverity); db.AddInParameter(cmd, "@Exception", DbType.String, exceptionInfo); db.ExecuteNonQuery(cmd, transaction); } } } |
其中在类的初始化的时候获取配置文件的默认数据库对象,通过重写TraceData方法来调用ExecuteSQL方法来执行异常日志插入。 在ExecuteWriteLogSQL方法中有句代码:
1 |
writeLogSQL = (string)this.Attributes["writeLogSQL"]; |
这个代码就是从配置文件中Listener的Attributes中获取所配置的执行SQL语句(这里不同于Logging模块自带的数据库以存储过程的记录方式,而是使用配置的SQL语句的方式,因为本项目是面向多数据库的,并不是所有的数据库都有存储过程的,比如SQLite),下面看下具体的配置信息: 配置文件创建主要分为以下2步: 1、在企业库的配置工具添加一个Exception Handle模块,然后添加一个名为Exception Policy的策略,再为这个策略添加异常类型,默认我选择所有异常类型(All Exceptions),Post Handle Action为: NotifyRethow(对不理解Post Handle Action的处理方式的可以看下下面的解释) PostHandlingAction 决定了在异常处理链完成后将发生什么活动。默认情况下,PostHandlingAction 被设置为 NotifyRethrow 。 None:应用程序块为此异常执行所有的处理程序,然后在 HandleException 方法的调用点上返回 false 给应用程序。应用程序检查此值以继续运行。 NotifyRethrow:应用程序块为此异常执行所有的处理程序,然后在 HandleException 方法的调用点上返回 true 给应用程序。应用程序检查到此值就重新抛出原始异常。 ThrowNewException:应用程序块为此异常执行所有的处理程序,然后在所有处理程序运行后抛出存在的异常。 2、为异常策略创建处理方式,我这边选择Loggin Exception Handler(在创建的同时配置工具会我们自动创建好Logging模块,并自动创建了一个日志分类:General,不过这个日志分类的默认Listener为event log,就是记录到系统的事件中),这时我们再创建一个CustomerTraceListener选择From File->自定义Listener所在DLL。 这边我碰到了一个问题就是添加了CustomerTraceListener,在对话框中我点击From File选择我编写的自定义Listener所在DLL,可惜没任何反应,不知道是不是要在DLL中做什么处理,所以我只能采用老办法:手写配置文件 首先看下Exception Handle模块的配置信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<exceptionHandling> <exceptionPolicies> <add name="ExceptionPolicy"> <exceptionTypes> <add name="All Exceptions" type="System.Exception, mscorlib, Version=4.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="General" 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> |
接下来是日志模块配置,在日志模块下我配置了3个Listener,其中Custom Trace Listener为我自定义的异常日志记录,Event Log Listener(系统日志记录)和Rolling Flat File Trace Listener(文本文件记录,按天回滚记录)为在日志分类General无法正常记录日志时的记录下日志分类General为何无法记录,因为异常日志默认保存到数据库中,但是如果数据库中存在问题,或者链接被关闭这时就无法正常记录异常,所以:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<loggingConfiguration name="" tracingEnabled="true" defaultCategory="General"> <listeners> <add listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.CustomTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging" writeLogSQL="insert into ExceptionLog(Message,LogDate,ExceptionLevel,Exception) values(@Message,@LogDate,@Level,@Exception)" type="EntLibStudy.Helper.EntLibExtension.ExceptionExtension.ExceptionCustomerListener, EntLibStudy.Helper, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" traceOutputOptions="None" name="Custom Trace Listener" initializeData="" formatter="Text Formatter" /> <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" /> <add name="Rolling Flat File Trace Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.RollingFlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.RollingFlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" fileName="rolling.log" formatter="Text Formatter" rollInterval="Day" /> </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}&#xA;Message: {message}{newline}&#xA;Category: {category}{newline}&#xA;Priority: {priority}{newline}&#xA;EventId: {eventid}{newline}&#xA;Severity: {severity}{newline}&#xA;Title:{title}{newline}&#xA;Machine: {localMachine}{newline}&#xA;App Domain: {localAppDomain}{newline}&#xA;ProcessId: {localProcessId}{newline}&#xA;Process Name: {localProcessName}{newline}&#xA;Thread Name: {threadName}{newline}&#xA;Win32 ThreadId:{win32ThreadId}{newline}&#xA;Extended Properties: {dictionary({key} - {value}{newline})}" name="Text Formatter" /> </formatters> <categorySources> <add switchValue="All" name="General"> <listeners> <add name="Custom Trace Listener" /> </listeners> </add> </categorySources> <specialSources> <allEvents switchValue="All" name="All Events" /> <notProcessed switchValue="All" name="Unprocessed Category" /> <errors switchValue="All" name="Logging Errors &amp; Warnings"> <listeners> <add name="Event Log Listener" /> <add name="Rolling Flat File Trace Listener" /> </listeners> </errors> </specialSources> </loggingConfiguration> |
在配置完后我们就可以进行代码编写,在页面里进行异常控制。 在ASP.NET中,异常处理主要有4种,执行顺序为:Page_Error事件>ErrorPage属性>Application_Error事件> <customErrors>,我这边采用Page_Error,由于在本项目中我已经建立了BasePage,所有的页面都继承这个页面,所以我只需在这个页面中编写Page_Error事件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
protected void Page_Error(object sender, EventArgs e) { //获取最新的异常信息 var ex = Server.GetLastError(); //处理异常 HandleException(ex, "ExceptionPolicy"); //清空异常 Server.ClearError(); } /// <summary> /// 异常处理方法 /// </summary> /// <param name="ex">异常信息</param> /// <param name="policy">异常处理策略</param> protected void HandleException(Exception ex, string policy) { bool rethrow = false; var exManager = EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>(); rethrow = exManager.HandleException(ex, policy); if (rethrow) { this.RedirectPermanent("~/error.aspx"); } } |
其中exManager.HandleException(ex, policy)为根据策略名处理异常,我这边使用的ExceptionPolicy,这个策略的处理方式为异常日志记录,它会帮我们调用到我们自定义的ExceptionCustomerListener 类,进行异常日志记录。 这样我们就完成了统一捕获系统中发生的异常了,本文也到此结束,欢迎大家指点! ——————————--文章扩展分割线———————————————-- 当然企业库中的Exception Handle和Logging模块远不止这些,Exception Handle还提供了异常替换(将指定的异常替换成其他的异常信息),异常包装(将一个异常包装到另外一个异常当中) Logging模块提供了许多的记录方式,如文本,XML,邮件,消息队列等等,所以我们可以根据我们的需求自由的选择。 本文仅仅就我的实际项目需求进行了简单的扩展,所以可能还有许多的不足,大家可根据自己的需求进行研究扩展,如果大家有好的异常记录处理办法可以提出来让我借鉴下。:) PS:我在文中提到了企业库的Logging模块太繁琐,大家可以看下我的使用log4net完成程序异常日志记录(使用SQLite数据库记录和普通文本记录)这篇文章,如果仅仅是要进行系统的异常记录的话log4net是个不错的选择,配置又方便而且也轻便,单若想完整使用企业库的功能的话就还是使用Exception Handle+Logging这个组合了。 注意: 1、MSSQL数据库在DataBase目录下(需要自行附加数据库),SQLite数据库在Web目录的App_Data下,由于考虑到项目的大小,所以每个项目的BIN目录都已经删除,如出现无法生成项目请自行添加相关企业库的DLL。 2、由于微软企业库5.0 学习之路这个系列我是准备以一个小型项目的形式介绍企业库的各模块,所以源代码会根据系列文章的更新而更新,所以源代码不能保证与文章中所贴代码相同。 3、项目开发环境为:VS2010+SQL2005。 4、管理员帐户:admin 密码:admin 源代码下载地址:点我下载 转自:http://www.cnblogs.com/kyo-yo/archive/2010/06/17/Learning-EntLib-Third.html
View Details在通过上一篇文章:[EntLib]微软企业库5.0 学习之路——第一步、基本入门 对EntLib5.0的相关知识进行了了解,并通过微软给出的学习例子我们可以了解企业库的各模块的基本用法(由于园子里的好几位兄弟都已经把企业库的各模块的使用都介绍过了,所以我这边就不进行具体功能的介绍,我写的学习之路是以企业库如何在一个项目中的使用为基础来介绍,以一个简单的学生信息管理系统项目为基础,如何使用企业库来对这个项目进行开发、优化)。 (本文内容比较简单,高手们可以直接忽略不看了,仅仅就是数据库访问操作和工厂分层) 现在我就开始进入学习之路的第二步——Data Access模块,这个模块是企业库中被使用频率最高的模块,它很好的封装了数据库操作应用,为我们进行多数据库系统开发提供了便利,只需更改配置文件就可以很快的切换数据库访问(可惜还是要重写SQL语句,没法和ORM比)。 下面是我在配置企业库的时候碰到问题,如果没有碰到可以略去不看(可能有点小白) 注意:此处切换数据库配置必须是计算机中已经安装好相应的数据库访问模块,如需要进行从MS SQL向SQLite数据库的变更时,计算机中必须安装好SQLite数据库访问模块(在这里我就碰到了这个问题,原来我机器上在VS2008开发时已经安装过SQLite数据库访问模块,但是新装了VS2010,在VS2010引用对话框中也能访问到在VS2008安装的SQLite(但是在企业库5.0配置器中无法查看到SQLite),但是发现更改企业库的配置文件后无法访问SQLite数据库,尝试了很多方法都没用,结果死马当活马医又重新装了一遍SQLite数据库访问模块再重新打开企业库配置器就可以看到SQLite数据库了(所以请确保在企业库编辑器中可以查看到要切换的数据库,否则可能导致无法访问数据库)。看下图: 回归正题,这次的学习由于VS2010发布了,而且企业库5.0也都支持.NET4.0,所以决定企业库的学习之路采用VS2010进行学习(顺便熟悉下.NET4的特性,毕竟公司的项目不可能立马转移到.NET4.0的,现在就当练手吧) 好了,现在就开始进行第2步的学习了,首先看下项目的结构: 项目采用仿MS PetShop架构,如不了解此架构可以到此查看了解:PetShop的系统架构设计 其中DAL和DALSQLite层对应MS SQL和SQLite数据库,Helper为整个项目的帮助器 现在来具体了解下DAL层 在DAL层中引用了Helper,IDAL,EnterpriseLibrary.Common和EnterpriseLibrary.Data这4个项目,其中Helper项目中有个DBHelper.cs,用于获取当前的数据对象,其代码如下(采用了C#4.0的语法特性,默认参数,数据库对象名默认为空,这样则会调用企业库默认的数据库对象,同时也可以在调用的时候赋值,这样则根据传递过来的数据库对象名来创建数据库,通过这个参数我们将原来需要重载的2个方法才能实现合并成了一个方法):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration; using Microsoft.Practices.EnterpriseLibrary.Data; namespace EntLibStudy.Helper { public static class DBHelper { /// <summary> /// 获取数据库对象 /// </summary> /// <param name="name">数据库实例名(默认name为空,调用默认数据库实例)</param> /// <returns>数据库对象</returns> public static Database CreateDataBase(string name = "") { //return DatabaseFactory.CreateDatabase(name); return EnterpriseLibraryContainer.Current.GetInstance<Database>(name); } } } |
在DAL层中则引用Helper来获取数据库对象,进行数据库操作,我们现在来看下具体的数据库访问类编写代码,学员操作类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Linq; using System.Text; using Microsoft.Practices.EnterpriseLibrary.Data; using EntLibStudy.Model; using EntLibStudy.Helper; namespace EntLibStudy.DAL { public class StudentService : EntLibStudy.IDAL.IStudentService { /// <summary> /// 新增学生 /// </summary> /// <param name="student">学生对象</param> /// <returns></returns> public int Add(Student student) { Database db = DBHelper.CreateDataBase(); StringBuilder sb = new StringBuilder(); sb.Append("insert into Student values(@ClassId,@SID,@Password,@Name,@Sex,@Birthday,@IsAdmin);SELECT @@IDENTITY;"); DbCommand cmd = db.GetSqlStringCommand(sb.ToString()); db.AddInParameter(cmd, "@ClassId", DbType.String, student.ClassId); db.AddInParameter(cmd, "@SID", DbType.String, student.Sid); db.AddInParameter(cmd, "@Password", DbType.String, student.Password); db.AddInParameter(cmd, "@Name", DbType.String, student.Name); db.AddInParameter(cmd, "@Sex", DbType.Int32, student.Sex); db.AddInParameter(cmd, "@Birthday", DbType.DateTime, student.Birthday); db.AddInParameter(cmd, "@IsAdmin", DbType.Int32, student.IsAdmin); int id = Convert.ToInt32(db.ExecuteScalar(cmd)); return id; } /// <summary> /// 更新 /// </summary> /// <param name="classInfo">学生对象</param> /// <returns>是否成功</returns> public bool Update(Student student) { Database db = DBHelper.CreateDataBase(); StringBuilder sb = new StringBuilder(); sb.Append("update Student set ClassId=@ClassId,"); sb.Append("SID=@SID,"); sb.Append("Password=@Password,"); sb.Append("Name=@Name,"); sb.Append("Sex=@Sex,"); sb.Append("Birthday=@Birthday,"); sb.Append("IsAdmin=@IsAdmin "); sb.Append(" where ID=@ID"); DbCommand cmd = db.GetSqlStringCommand(sb.ToString()); db.AddInParameter(cmd, "@ClassId", DbType.String, student.ClassId); db.AddInParameter(cmd, "@SID", DbType.String, student.Sid); db.AddInParameter(cmd, "@Password", DbType.String, student.Password); db.AddInParameter(cmd, "@Name", DbType.String, student.Name); db.AddInParameter(cmd, "@Sex", DbType.Int32, student.Sex); db.AddInParameter(cmd, "@Birthday", DbType.DateTime, student.Birthday); db.AddInParameter(cmd, "@IsAdmin", DbType.Int32, student.IsAdmin); db.AddInParameter(cmd, "@ID", DbType.Int32, student.Id); return db.ExecuteNonQuery(cmd) > 0 ? true : false; } /// <summary> /// 删除 /// </summary> /// <param name="id">学生ID</param> /// <returns>是否成功</returns> public bool Delete(int id) { Database db = DBHelper.CreateDataBase(); StringBuilder sb = new StringBuilder(); sb.Append("delete from Student "); sb.Append(" where ID=@ID"); DbCommand cmd = db.GetSqlStringCommand(sb.ToString()); db.AddInParameter(cmd, "@ID", DbType.Int32, id); return db.ExecuteNonQuery(cmd) > 0 ? true : false; } /// <summary> /// 根据学生ID查询学生对象 /// </summary> /// <param name="id">学生ID</param> /// <returns></returns> public Student SelectById(int id) { Student student = null; Database db = DBHelper.CreateDataBase(); StringBuilder sb = new StringBuilder(); sb.Append("select * from Student "); sb.Append(" where ID=@ID"); DbCommand cmd = db.GetSqlStringCommand(sb.ToString()); db.AddInParameter(cmd, "@ID", DbType.Int32, id); using (IDataReader reader = db.ExecuteReader(cmd)) { if (reader.Read()) { student = new Student() { Id = reader.GetInt32(0), ClassId = reader.GetInt32(1), Sid = reader.GetString(2), Password = reader.GetString(3), Name = reader.GetString(4), Sex = reader.GetInt32(5), Birthday = reader.GetDateTime(6), IsAdmin = reader.GetInt32(7) }; } } return student; } /// <summary> /// 查询所有学生信息 /// </summary> /// <returns></returns> public IList<Student> SelectAll() { List<Student> list = new List<Student>(); Database db = DBHelper.CreateDataBase(); StringBuilder sb = new StringBuilder(); sb.Append("select * from Student "); DbCommand cmd = db.GetSqlStringCommand(sb.ToString()); using (IDataReader reader = db.ExecuteReader(cmd)) { while (reader.Read()) { list.Add(new Student() { Id = reader.GetInt32(0), ClassId = reader.GetInt32(1), Sid = reader.GetString(2), Password = reader.GetString(3), Name = reader.GetString(4), Sex = reader.GetInt32(5), Birthday = reader.GetDateTime(6), IsAdmin = reader.GetInt32(7) }); } } return list; } /// <summary> /// 查询所有学生信息 /// </summary> /// <returns></returns> public IList<Student> SelectAllMapper() { var list = new List<Student>(); Database db = DBHelper.CreateDataBase(); DataAccessor<Student> studentAccessor; //studentAccessor = db.CreateSqlStringAccessor("select * from Student", // MapBuilder<Student>.MapAllProperties(). // Build() // ); studentAccessor = db.CreateSqlStringAccessor("select * from Student", MapBuilder<Student>.MapAllProperties(). Map(p => p.Id).ToColumn("ID"). Map(p => p.Sid).ToColumn("SID"). Map(p => p.Password).WithFunc(f => "******").//将密码转换为"*",无法直接查看 Map(p => p.Name).WithFunc(ToUpperName).//将学员名称转换为大写 Map(p => p.Sex).ToColumn("Sex"). Map(p => p.Birthday).ToColumn("Birthday"). Build() ); list = studentAccessor.Execute().ToList(); return list; } /// <summary> /// 将学员名称转换为大写 /// </summary> /// <param name="dataRecord"></param> /// <returns></returns> private string ToUpperName(IDataRecord dataRecord) { var name = (string)dataRecord["Name"]; return name.ToUpper(); } public Student SelectBySid(string sid) { Student student = null; Database db = DBHelper.CreateDataBase(); StringBuilder sb = new StringBuilder(); sb.Append("select * from Student "); sb.Append(" where SID=@SID"); DbCommand cmd = db.GetSqlStringCommand(sb.ToString()); db.AddInParameter(cmd, "@SID", DbType.String, sid); using (IDataReader reader = db.ExecuteReader(cmd)) { if (reader.Read()) { student = new Student() { Id = reader.GetInt32(0), ClassId = reader.GetInt32(1), Sid = reader.GetString(2), Password = reader.GetString(3), Name = reader.GetString(4), Sex = reader.GetInt32(5), Birthday = reader.GetDateTime(6), IsAdmin = reader.GetInt32(7) }; } } return student; } } } |
其中的代码都是采用了比较常见的老套路: 1、获取数据库对象 2、构建Command对象并进行执行语句及参数赋值 3、通过数据库对象调用相应方法执行Command 企业库在Data Access上帮我们做了比较好的封装,相当于为我们提供了如SQLHelper,OracleHelper类,只不过这个帮助类转换了一个个数据库的对象,通过数据库对象来对数据库数据进行操作 (个人认为通过这种方式进行操作更加直观,而且企业库的对SQL语句的参数操作方法也很直观:AddInParameter,AddOutParameter,GetParameterValue很好的区分了参数的操作,比原来的SQLCommand好多了) 如果仔细看了上面操作代码的朋友肯定发现了类中有个叫SelectAllMapper的方法,这个方法采用的是企业库5.0中新提供的Accessor进行RowMapper来直接为实体赋值,相比原来的使用reader方式取值赋值更加优雅,只要SQL查询出来的对象字段和实体对象属性一样就可以使用MapAllProperties()方法直接赋值,如果不同的话可以使用map方法来对个别属性单独映射,而且在映射的时候还可以使用WithFunc来进行进一步操作,在代码中我将密码进行了替换,以“*”的形式展示,同时把学员的名称以大写的形式展示。 (注:更多的企业库Data Access模块方法使用可以点击这里下载微软给出的学习例子和http://www.entlib.com/发布的学习手册) 在完成底层的操作,现在我们就开始对企业库的数据库访问进行配置: 在Web层的Web.config上右键打开企业库配置器:Blocks-Add Data Settings-Add DataBase Connstring,新建2个数据库对象,一个是MS SqlServer数据库,一个是SQLite数据库. 新建一个数据库设置 新建二个数据库连接 一个为EntLibStudy,另一个为EntLibSQLite 我们来看下具体的配置文件代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<configuration> <configSections> <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" /> </configSections> <dataConfiguration defaultDatabase="EntLibStudy" /> <connectionStrings> <add name="EntLibStudy" connectionString="server=VOLK\SQL2005;database=EntLibStudy;Integrated Security=True;" providerName="System.Data.SqlClient" /> <add name="EntLibStudySQLite" connectionString="data source=|DataDirectory|EntLibStudySQLite.db3" providerName="System.Data.SQLite" /> </connectionStrings> <appSettings> <add key="DAL" value="EntLibStudy.DAL" /> </appSettings> <system.web> <compilation debug="true" targetFramework="4.0" > <expressionBuilders> <add expressionPrefix="RouteUrl" type="System.Web.Compilation.RouteUrlExpressionBuilder"/> <add expressionPrefix="RouteValue" type="System.Web.Compilation.RouteValueExpressionBuilder"/> </expressionBuilders> </compilation> <authentication mode="Forms"> </authentication> </system.web> </configuration> |
至此我们就完成了Data Access模块的代码编写和基本设置(具体代码请到文章底部下载源代码,类似代码则不再描述)。 这时如果项目需求发生了变更,由于成本太高不能使用MS SQL SERVER,而要改用SQLite数据库时则只需更改配置文件,将dataConfiguration配置节中defaultDatabase更改为EntLibStudySQLite,将appSettings配置节中DAL的值改为EntLibStudy.DALSQLite即可立刻切换到SQLite数据库。 下面我来看下运行出来的效果: 红色框中地址采用了ASP.NET4中的路由功能,实现了地址重写 红框中如上面的分析,在DAL层进行属性映射的时候已经将密码以*代替,学员姓名以大写形式展现(此页面仅用来展示属性映射,无其他用处,页面地址为:~/Student/StudentList.aspx) 至此,学习之路的第二步——Data Access模块的学习就到此为止了,其他一些东西请下载源代码查看。 注意: 1、MSSQL数据库在DataBase目录下(需要自行附加数据库),SQLite数据库在Web目录的App_Data下,由于考虑到项目的大小,所以每个项目的BIN目录都已经删除,如出现无法生成项目请自行添加相关企业库的DLL。 2、由于微软企业库5.0 学习之路这个系列我是准备以一个小型项目的形式介绍企业库的各模块,所以源代码会根据系列文章的更新而更新,所以源代码不能保证与文章中所贴代码相同。 3、项目开发环境为:VS2010+SQL2005。 4、管理员帐户:admin 密码:admin 项目下载地址:点我下载 转自:http://www.cnblogs.com/kyo-yo/archive/2010/06/10/Learning-EntLib-Second.html
View Details话说在大学的时候帮老师做项目的时候就已经接触过企业库了但是当初一直没明白为什么要用这个,只觉得好麻烦啊,竟然有那么多的乱七八糟的配置(原来我不知道有配置工具可以进行配置,请原谅我的小白)。 直到去年在做项目的时候在看代码生成工具的时候看到了许多生成工具都内置了企业库的数据库访问及一些相关模块,才突然顿悟了,可能企业库应该还是有点用的,于是就试着使用了企业库的一些模块(当时版本是4.1),果然觉得企业库还是很不错的,微软在企业库中为我们封装了许多日常经常使用的模块,如:数据库访问,缓存,日志,异常处理,加密,IOC等等,在日常的项目开发中,企业库已经贯穿了一个项目从底层到表示层基本上需要用到的模块,大大方便了我们的开发,毕竟自己再重复造轮子还不如就用微软的(毕竟企业库现在都到5.0) 最近在看园子里有好几位都在写,如:virus,huangcong。我现在写的其实基本上都是自己的学习之路(可能没前面2位写的好,但就当自己的学习笔记吧)。 废话不多说了,首先开始学习企业库的基本概念,一下摘自sfbirp的企业库学习笔记: 一、什么是企业库? 企业库包含一系列应用程序模块和核心架构。这些高复用的组件旨在帮助开发者解决一些共同的企业开发问题。 企业库同时提供高可配置的特性,使我们管理重复的工作更加容易,例如我们知道的在应用的很多地方发生的横切关注点。包括像日志记录、缓存、异常管理等。另外,它提供的依赖注入容器能够简化和分解你的设计,使他们更加可实验和容易理解,帮助你创建更加高性能的设计和各种应用。 二、使用企业库的3个简单步骤: 1.引用和配置你需要的程序集。 2.通过企业库配置应用。 3.在你的代码中创建和使用企业库对象。 三、企业库的好处: 应用模块帮助解决开发者从一个项目到另一个项目面对的共同问题。他们的设计封装了微软推荐的做法,这些都是基于微软框架的应用开发。例如,数据访问应用模块提供了对ADO.NET访问最频繁使用的特征。在某些情况下,应用模块还添加了一些基础类库没有直接提供的相关功能。 四、企业库的目标: 1.一致。所有的企业库模块都坚持一致的设计模式和实现方式。 2.可扩展性。所有的应用模块包括定义扩展点,允许开发人员通过添加自己的代码定制应用模块的行为。 3.易用性。企业库提供了许多实用性的东西,包括一个图形化配置工具,简单的安装过程,完成的文档和示例。 4.集成。企业库应用模块被设计得能够一起很好的工作,并且也被这样测试过。但是你不必一起使用他们。我们可以单独使用这些应用模块,同时这些应用模块之间也有些依赖,比如在企业库核心和Unity中的一些通用组件。 上面是企业库的基本概念,理解了企业库的相关知识后,我们可以开始来安装企业库了 1、下载地址:点我进入下载页面(不是直接下载),安装后就可以使用了。 这次5.0相比4.1的最大改动就是整个配置工具采用了WPF重新构建和实例化和管理对象生命周期的全依赖注入的实现,同时支持VS2008SP1和VS2010,话说虽然这次的配置工具变化挺大的,但是一旦熟悉了就觉得比4.1的好,因为可以清楚的看见每个模块的之间的依赖关系。 附相关改动,还是摘自sfbirp的企业库学习笔记: 一、Unity和对象生成器的整合 在这个版本中,用于创建对象的基本技术是一个单一的依赖注入容器,默认的是Unity。你可以使用容器生成企业库对象的实例并注入到其他的对象。 企业库提供一个标准的接口从Unity容器中来获得定义在企业库配置中的对象的实例,如SqlDatabase或LogWriter.另外,注入友好的实例门面处理静态门面之外是有效的,因为静态门面不能用注入,但是为了向后兼容以前的版本而存在。在本版本中的示例中都是用依赖注入,但是以前版本中是用的静态工厂类和静态门面在这个版本中还是支持的。对象生成器,一个低版本的依赖注入机制在这个版本中被归入Unity中,在项目中不再需要单独引用对象生成器集。 支持Unity容器,必须引用一个被包含在企业库中的Microsoft.Practices.ServiceLocation程序集。如果要使用一个第3方的依赖注入容器,必须直接实现IServiceLocator接口或者通过适配器实现。 二、影响所有模块的变化: 1.在企业库中主要修正是使用依赖注入机制。所用的应用模块以及核心系统都是用依赖注入机制,并使用Unity作为默认的依赖注入容器,来创建和管理企业库对象。 2.自主容器的实现通过实现Common Service Locator项目提供的 IServiceLocator 接口来完成。 3.由于错误配置引发的错误将提供更多有用的错误信息。 4.配置系统公开了一个 fluent接口,用来为单个对象或整个应用创建和填充配置源。fluent API使得为各种情景创建配置源更加容易。 5.ConfigurationView类被删除. 6.一些配置元素有默认值,可能不同于以前版本的隐式默认值. 7.企业库现在允许你通过另一个配置文件合成一个混合配置文件. 8.可以可通过不同的配置文件读取不同的配置信息. 9.企业库不支持XAML浏览器应用程序(XBAP). 10.WmiEnabled标志为了像前兼容仍然存在企业库中,但是在5.0中是被忽略的,而且将来会被删除. 11.改进式的安装允许你只安装部分应用模块及配置工具. 12.在以前版本中要做统一集成,必须添加核心企业库的扩展和每个模块的扩展。现在如果你只需要直接访问容器,那么只有核心扩展是必须的。单独模块将自动支持。 旧的功能为了保持像前兼容仍然保留,但已经不起作用。 13.FileConfigurationSource.Save 的签名已经改变,参数由3个变为2个。 14.快速入门不再包含在主安装程序中。 三、Breaking变化: 1.企业库现在抛出了一个配置错误ActivationException,之前是System.Configuration.ConfigurationErrorsException。这包括试着解决没有配置信息错误的一个实例提供者。 2.以前版本在获取应用模块错误时抛出BuildFailedException错误,现在对于所有的应用模块都抛出ActivationException 3 .之前的版本,在讲一个空源传到容器来调用容器的时候,会抛出ArgumentNullException,现在抛出NullReferenceException 4.ObjectBuilder2不再是一个单独的程序集,而是集成到了Unity集合中,在项目中也不需要引用ObjectBuilder2.dll。 5.WMI支持已经从企业库中删除,除了在logging模块中的WMI跟踪监听器。 6.如果你没有关闭DbDataReader,可能会导致随机的、很难在您的代码中找到的错误,尤其是当你正在一个由TransactionScope上下文创建的隐式事务下操作时。 你必须始终确保您的应用程序及时关闭DbDataReader,无论是明确的DbDataReader.Close方法关闭或是逼迫DbDataReader释放。 7.如果你使用 validator 特性必须引用 System.ComponentModel.DataAnnotations程序集。 8.为FileConfigurationSource.Save方法签名已更改。该方法有两个参数,而不是3个参数 9.Microsoft.Practices.EnterpriseLibrary.Configuration.Design.dll集合的功能和其他设计时集合被一个新的集合Microsoft.Practices.EnterpriseLibrary.Configuration.DesignTime.dll代替。 10,性能计数器异常从PolicyInjection.CallHandlers移到 PolicyInjection 程序集。 11.包含在Policy Injection Application Block中的CachingCallHandler有未处理的安全漏洞,已经从Policy Injection Application Block中移除。 四、配置工具的改变: 1.新的企业拥有一个新的GUI库和一个元数据驱动的可扩展性模。 2.支持向导 3.新的类型选择。 4.不支持对依赖策略的Environmental Overrides 。日志模块处理Categories。 五、缓存模块变化: 1.缓存清除已被完全重写的性能优化 六、数据库访问模块: 1.ExecuteReader, ExecuteXmlReader, ExecuteScalar, and ExecuteNonQuery方法具有异步版本。 2.包含了很多新的方法和类允许你提取数据作为对象序列。例如在合适的时候使用客户端查询技术,如LINQ. 3.存在的方法ExecuteReader和新的方法BeginExecuteReader不接收CommandBehavior 参数。默认的当调用这些方法的时候这些方法 会自动设置CommandBehavior 属性到reder中用来关闭连接直到指定一个事务。 七、异常处理模块: 1.日志异常处理重新使用日志模块的Log Writer and Trace Listeners 。这在之前版本中不是默认设置。 2.增加一个功能,通过ExceptionManager.Process 方法接收一个默认值并返回一个值。 在安装完企业库同时知道了5.0的变化后,就开始正式的学习了,学习的最好办法不是看企业库的源代码(我是看不懂),而是下载微软发布的学习例子 学习例子下载地址:点我进入下载页面(不是直接下载) 这个学习例子里面已经包含了所需的类库及代码演示(C#和VB.NET都有),同时可以右键例子中的App.config来看下例子中的模块是怎么配置的。 同时,在学习例子的时候可以看下由http://www.entlib.com/发布的中文学习手册,虽然是4.1的,但是里面的所有内容都和5.0的例子一样(5.0只不过是重新实现了1遍),把例子和文档结合起来看学习起来就快了很多了: 中文学习手册:点我下载 Entlib5程序:点我进入下载页面(不是直接下载), 到此,Entlib5的学习之路的第一步就结束了,基本上通过微软发布的学习例子和对应的中文学习手册可以对5.0的使用已经有了个基本的了解。 转自:http://www.cnblogs.com/kyo-yo/archive/2010/06/07/Study-Entlib5-First.html
View Details