#眉標=Enterprise Library
#副標=設計模型套件系列(11)
#大標=資料驗証機制(下)
#作者=文/圖 王寧疆



==<box>===========
程式1
public class ValidateAttributeProduct
{
[RangeValidator(0, RangeBoundaryType.Inclusive, 1000, RangeBoundaryType.Inclusive, 
MessageTemplate="ProductID必須是介於1~1000的數字", Ruleset = "AttributeRuleSet")]
    public int ProductID;		//驗証ProductID變數的內容必須是介於1~1000的數字
    [StringLengthValidator(1, 4, MessageTemplate = "ProductName的長度必須是介於1~4個字元的字串", 
Ruleset = "AttributeRuleSet")]
    public string ProductName;		//驗証ProductName變數的內容的長度必須是介於1~4個字元的字串
    [RangeValidator(0, RangeBoundaryType.Inclusive, 100, RangeBoundaryType.Inclusive, 
MessageTemplate = "SupplierID必須是介於1~100的數字", Ruleset = "AttributeRuleSet")]
    public int SupplierID;				//驗証SupplierID變數的內容必須是介於1~100的數字
    [RangeValidator(0, RangeBoundaryType.Inclusive, 100, RangeBoundaryType.Inclusive, 
MessageTemplate = "CategoryID必須是介於1~100的數字", Ruleset = "AttributeRuleSet")]
    public int CategoryID;				//驗証CategoryID變數的內容必須是介於1~100的數字
    [StringLengthValidator(1, 20, MessageTemplate = "QuantityPerUnit的長度必須是介於1~4個字元的字串", 
Ruleset = "AttributeRuleSet")]
    public string QuantityPerUnit;	//驗証"QuantityPerUnit變數內容的長度必須是介於1~4個字元的字串
    [NotNullValidator(MessageTemplate="UnitPrice必須有內容值", Ruleset = "AttributeRuleSet")]
    public decimal UnitPrice;							//驗証UnitPrice變數必須有內容值
    [RangeValidator(0, RangeBoundaryType.Inclusive, 100, RangeBoundaryType.Inclusive, 
MessageTemplate = "UnitsInStock必須是介於1~100的數字", Ruleset = "AttributeRuleSet")]
    public int UnitsInStock;				//驗証UnitsInStock變數的內容必須是介於1~100的數字
    [RangeValidator(0, RangeBoundaryType.Inclusive, 100, RangeBoundaryType.Inclusive, 
MessageTemplate = " UnitsOnOrder必須是介於1~100的數字", Ruleset = "AttributeRuleSet")]
    public int UnitsOnOrder;				//驗証UnitsOnOrder變數的內容必須是介於1~100的數字
    [RangeValidator(0, RangeBoundaryType.Inclusive, 100, RangeBoundaryType.Inclusive, 
MessageTemplate = "ReorderLevel必須是介於1~100的數字", Ruleset = "AttributeRuleSet")]
    public int ReorderLevel;				//驗証ReorderLevel變數的內容必須是介於1~100的數字
    [NotNullValidator(MessageTemplate="Discontinued必須有內容值", Ruleset = "AttributeRuleSet")]
    public bool Discontinued;							//驗証Discontinued變數必須有內容值
}

==<end>==============





==<box>===========
程式2
string strConn = "Data Source=.;Initial Catalog=Northwind;Integrated Security=True";	//資料庫連線資訊

public ValidateAttributeProduct GetProduct(int ProductID, ref string strError)
{
    string strSQL = "Select * from Products where ProductID=@ProductID";	   //查詢商品資訊的SQL敘述
    SqlConnection conn = new SqlConnection(strConn);				//建立SqlConnection類別的物件
    SqlCommand cmd = new SqlCommand(strSQL, conn);			//建立SqlCommand類別的物件
    cmd.CommandType = CommandType.Text;	   //設定SqlCommand類別的物件的CommandType屬性
    cmd.Parameters.AddWithValue("@ProductID", ProductID);			//設定@ProductID參數的內容值

    conn.Open();												//開啟資料庫連線
    SqlDataReader dr = cmd.ExecuteReader();//呼叫SqlCommand類別的ExecuteReader方法對資料庫執行查詢
    dr.Read();										//讀取查詢得到的第一筆記錄
    ValidateAttributeProduct p = new ValidateAttributeProduct();	//建立ValidateAttributeProduct類別的物件
    p.ProductID = int.Parse(dr["ProductID"].ToString());			//設定物件的ProductID屬性的內容值
    p.ProductName = dr["ProductName"].ToString();		   //設定物件的ProductName屬性的內容值
    p.SupplierID = int.Parse(dr["SupplierID"].ToString());			//設定物件的SupplierID屬性的內容值
    p.CategoryID = int.Parse(dr["CategoryID"].ToString());		//設定物件的CategoryID屬性的內容值
    p.QuantityPerUnit = dr["QuantityPerUnit"].ToString();	//設定物件的QuantityPerUnit屬性的內容值
    p.UnitPrice = decimal.Parse(dr["UnitPrice"].ToString());		//設定物件的UnitPrice屬性的內容值
    p.UnitsInStock = Int16.Parse(dr["UnitsInStock"].ToString());	   //設定物件的UnitsInStock屬性的內容值
    p.UnitsOnOrder = Int16.Parse(dr["UnitsOnOrder"].ToString());//設定物件的UnitsOnOrder屬性的內容值
    p.ReorderLevel = Int16.Parse(dr["ReorderLevel"].ToString());  //設定物件的ReorderLevel屬性的內容值
    p.Discontinued = bool.Parse(dr["Discontinued"].ToString());	   //設定物件的Discontinued屬性的內容值

    dr.Close();										//關閉SqlDataReader類別的物件
    dr.Dispose();										//丟棄SqlDataReader類別的物件
    cmd.Dispose();									//丟棄SqlCommand類別的物件
    conn.Close();												//關閉資料庫連線
    conn.Dispose();								    //丟棄SqlCoonnection類別的物件

    Validator<ValidateAttributeProduct> validator=ValidationFactory.CreateValidator<ValidateAttributeProduct>
("AttributeRuleSet");	//建立可以依據名稱為AttributeRuleSet的驗証規則集合進
//行資料驗証的Validator物件
    ValidationResults results = validator.Validate(p);		//呼叫Validator類別的Validate方法驗証
    //ValidateAttributeProduct類別的物件的內容是否正確
if (results.IsValid)											//如果資料驗証成功
    {
        strError = "查詢成功!";							//顯示資料驗証成功的訊息
        return p;								//傳回ValidateAttributeProduct類別的物件
    }
    else
    {
        StringBuilder sb = new StringBuilder();		//建立存放驗証錯誤的訊息的StringBuilder物件
        foreach (ValidationResult result in results)						//取出每一個錯誤訊息
        {
            sb.Append(String.Format("- <b>{0}:{1}</b><br />", 
"ProductName", result.Message));			//設定錯誤訊息的顯示格式
        }
strError = sb.ToString();									//顯示錯誤訊息
        return null;												//傳回null
    }
}

==<end>==============




==<box>===========
程式3
Validator ProductNameValidator = new AndCompositeValidator(		//建立組合兩個Validator的
                            new NotNullValidator(),			//AndCompositeValidator類別的物件
                            new StringLengthValidator(1, 5));	
ValidationResults r = Validation.Validate(p.ProductName);		//驗証p的ProductName變數的內容是否正確
==<end>==============





==<box>===========
程式4
[HasSelfValidation]									//標示為擁有自我測試功能的類別
public class SelfValidateProduct
{
    public int ProductID;
    public string ProductName;
    public int SupplierID;
    public int CategoryID;
    public string QuantityPerUnit;
    public decimal UnitPrice;
    public int UnitsInStock;
    public int UnitsOnOrder;
    public int ReorderLevel;
    public bool Discontinued;

    [SelfValidation]									//負責執行自我測試的方法
    public void DoValidate(ValidationResults results)
    {
        if (string.IsNullOrEmpty(ProductName))				//如果ProductName變數沒有內容值
        {
            results.AddResult(new ValidationResult("ProductName不可空白!", this, "ProductName", 
null, null));		//填入新的錯誤訊息到名稱為results的參數中
        }
    }
}

==<end>==============





==<box>===========
程式5
protected void btnSelfValidate_Click(object sender, EventArgs e)
{
    SelfValidateProduct p = new SelfValidateProduct();			//建立SelfValidateProduct類別的物件
    p.ProductName = txtProductName.Text;					//設定物件的ProductName變數的內容
    p.SupplierID = int.Parse(txtSupplierID.Text); 				//設定物件的SupplierID變數的內容
    p.CategoryID = int.Parse(txtCategoryID.Text); 				//設定物件的CategoryID變數的內容
    p.QuantityPerUnit = txtQuantityPerUnit.Text;			//設定物件的QuantityPerUnit變數的內容
    p.UnitPrice = decimal.Parse(txtUnitPrice.Text);				//設定物件的UnitPrice變數的內容
    p.UnitsInStock = Int16.Parse(txtUnitsInStock.Text);			//設定物件的UnitsInStock變數的內容
    p.UnitsOnOrder = Int16.Parse(txtUnitsOnOrder.Text); 		//設定物件的UnitsOnOrder變數的內容
    p.ReorderLevel = Int16.Parse(txtReorderLevel.Text);			//設定物件的ReorderLevel變數的內容
    p.Discontinued = 
(txtDiscontinued.Text.Equals("True") ? true : false); 		//設定物件的Discontinued變數的內容

    ValidationResults results = 
Validation.Validate<SelfValidateProduct>(p);		//呼叫Validation類別的Validate方法
   //命令SelfValidateProduct類別的物件p進行自我驗証
    if (results.IsValid)											//如果驗証成功
    {
        lbMessage.Text="驗証成功!";							//顯示驗証成功的訊息
    }
    else															//否則
    {
        StringBuilder sb = new StringBuilder();					//建立StringBuilder類別的物件
        foreach (ValidationResult result in results)					//取出所有的驗証錯誤訊息
        {
            sb.Append(String.Format("- <b>{0}:{1}</b><br />", 
result.Key, result.Message));	//將錯誤訊息加入到StringBuilder類別的物件中
        }
        lbMessage.Text = sb.ToString();							//顯示所有的錯誤訊息
    }
}

==<end>==============



==<box>===========
程式6
namespace DataAccess
{
    class ASPNETIntegratedProduct
    {
        private int m_ProductName;		//存放ProductName屬性的內容值
        [StringLengthValidator(1, 5, 
     MessageTemplate = "ProductName的長度必須是介於1~5個字元的字串", 
     Ruleset = "IntegratedRuleset")]		//設定ProductName屬性的資料驗証規則
        public int ProductName		//ProductName屬性
        {
            get { return m_ProductName; }		//取用ProductName屬性的函式
            set { m_ProductName = value; }	//設定ProductName屬性的函式
        }
    }
}
==<end>==============




==<box>===========
程式7
protected void PropertyProxyValidator1_ValueConvert(object sender, 
     Microsoft.Practices.EnterpriseLibrary.Validation.Integration.ValueConvertEventArgs e)
{
e.ConvertedValue = e.ValueToConvert.ToString();	//轉型成字串之後填回e的參數的ValueToConvert屬性
}
==<end>==============




==<灰>===========
using System.Collections.Generic;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Validation.Configuration;
==<end>==============





==<box>===========
程式8
[ConfigurationElementType(typeof(CustomValidatorData))]    //加上ConfigurationElementType Attribute設定
public class GenderValidator : DomainValidator<string>		//設定類別要繼承自DomainValidator類別
{
    private static List<string> CorrectContent= new List<string>(new string[] {
            "男", "女", "Male", "Female"});						//指定可以接受的內容值
    public GenderValidator() : base(CorrectContent)			//將可以接受的內容值傳遞給父類別
    {
    }
    protected override string DefaultNonNegatedMessageTemplate	//override名稱為//DefaultNonNegatedMessageTemplate
//的屬性
    {
       get { return "資料的內容並非性別合法的內容值"; }		//傳回資料驗証失敗欲顯示的錯誤訊息
    }
}

==<end>==============




==<灰>===========
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
using Microsoft.Practices.EnterpriseLibrary.Validation;
==<end>==============

再製作一個繼承自ValidatorAttribute類別的子類別,並將類別編輯成程式9。之後就可以為類別加入驗証性別資料的內容是否正確的功能了。例如程式10的Employee類別就會利用上述的GenderValidatorAttribute類別驗証名稱為Gender的屬性是否具有合法的內容值。


==<box>===========
程式9
public class GenderValidatorAttribute : ValidatorAttribute		/設定類別要繼承自ValidatorAttribute類別
{
    protected override Validator DoCreateValidator(Type targetType)	//override DoCreateValidator方法
    {
        return new GenderValidator();	//建立負責執行資料驗証動作的GenderValidator類別
    }
}
==<end>==============



==<box>===========
程式10
public class Employee
{
    private string m_Gender;		//存放Gender屬性內容值的變數
    [GenderValidator(MessageTemplate="不合法的性別內容", 
Ruleset="GenderRule")]		//指定使用GenderValidator驗証屬性並將資料名稱設定為GenderRule
    public string Gender			//存放性別資訊的屬性
    {
        get { return m_Gender; }		//傳回性別的內容值
        set { m_Gender = value; }		//設定性別的內容值
     }	
}
==<end>==============






==<box>===========
程式11
protected void btnSubmit_Click(object sender, EventArgs e)
{
    Employee emp = new Employee();						//建立Employee類別的物件
    emp.Gender = txtGender.Text;							//填入使用者輸入的性別資訊
    ValidationResults vr=Validation.Validate<Employee>(emp, 
"GenderRule"); 	//建立驗証Employee類別的物件內容的Validator,指定要使用名稱為
//GenderRule的驗証規則集合進行資料驗証的動作
    if (vr.IsValid)											//如果資料驗証無誤
    {
        lbMessage.Text="資料驗証成功!";						//顯示資料驗証成功的訊息
    }
    else
    {
        StringBuilder sb = new StringBuilder();					//建立StringBuilder類別的物件
        foreach (ValidationResult result in vr)				//取出驗証失敗的每一個錯誤訊息
        {
            sb.Append(String.Format("- <b>{0}:{1}</b><br />", 
"Gender", result.Message));	         //將錯誤訊息加入到StringBuilder類別的物件中
        }
        lbMessage.Text = sb.ToString();						//將錯誤訊息顯示在網頁上
    }
}

==<end>==============