Attribute在.NET編程中的應用整理(四)

SqlCommandGenerator類的設計

SqlCommandGEnerator類的設計思路就是通過反射得到方法的參數,使用被SqlCommandParameterAttribute標記的參數來裝配一個Command實例。

引用的命名空間:

  1. //SqlCommandGenerator.cs   
  2.   
  3.   
  4.   
  5. using System;   
  6.   
  7. using System.Reflection;   
  8.   
  9. using System.Data;   
  10.   
  11. using System.Data.SqlClient;   
  12.   
  13. using Debug = System.Diagnostics.Debug;   
  14.   
  15. using StackTrace = System.Diagnostics.StackTrace;     
  16.   
  17.   
  18.   
  19. 類代碼:    
  20.   
  21. namespace DataAccess   
  22.   
  23. {   
  24.   
  25.    public sealed class SqlCommandGenerator   
  26.   
  27.    {   
  28.   
  29.       //私有構造器,不允許使用無參數的構造器構造一個實例   
  30.   
  31.       private SqlCommandGenerator()   
  32.   
  33.       {   
  34.   
  35.          throw new NotSupportedException();   
  36.   
  37.       }   
  38.   
  39.   
  40.   
  41.       //靜態只讀字段,定義用於返回值的參數名稱   
  42.   
  43.       public static readonly string ReturnValueParameterName = "RETURN_VALUE";   
  44.   
  45.       //靜態只讀字段,用於不帶參數的存儲過程   
  46.   
  47.       public static readonly object[] NoValues = new object[] {};   
  48.   
  49.       
  50.   
  51.          
  52.   
  53.       public static SqlCommand GenerateCommand(SqlConnection connection,   
  54.   
  55.                                   MethodInfo method, object[] values)   
  56.   
  57.       {   
  58.   
  59.          //如果沒有指定方法名稱,從堆棧幀得到方法名稱   
  60.   
  61.          if (method == null)   
  62.   
  63.              method = (MethodInfo) (new StackTrace().GetFrame(1).GetMethod());   
  64.   
  65.   
  66.   
  67.          // 獲取方法傳進來的SqlCommandMethodAttribute   
  68.   
  69.          // 爲了使用該方法來生成一個Command對象,要求有這個Attribute。   
  70.   
  71.          SqlCommandMethodAttribute commandAttribute =    
  72.   
  73.             (SqlCommandMethodAttribute) Attribute.GetCustomAttribute(method, typeof(SqlCommandMethodAttribute));   
  74.   
  75.   
  76.   
  77.          Debug.Assert(commandAttribute != null);   
  78.   
  79.          Debug.Assert(commandAttribute.CommandType == CommandType.StoredProcedure ||   
  80.   
  81.        commandAttribute.CommandType == CommandType.Text);   
  82.   
  83.   
  84.   
  85.          // 創建一個SqlCommand對象,同時通過指定的attribute對它進行配置。   
  86.   
  87.          SqlCommand command = new SqlCommand();   
  88.   
  89.          command.Connection = connection;   
  90.   
  91.          command.CommandType = commandAttribute.CommandType;   
  92.   
  93.          
  94.   
  95.          // 獲取command的文本,如果沒有指定,那麼使用方法的名稱作爲存儲過程名稱    
  96.   
  97.          if (commandAttribute.CommandText.Length == 0)   
  98.   
  99.          {   
  100.   
  101.             Debug.Assert(commandAttribute.CommandType == CommandType.StoredProcedure);   
  102.   
  103.             command.CommandText = method.Name;   
  104.   
  105.          }   
  106.   
  107.          else  
  108.   
  109.          {   
  110.   
  111.             command.CommandText = commandAttribute.CommandText;   
  112.   
  113.          }   
  114.   
  115.   
  116.   
  117.          // 調用GeneratorCommandParameters方法,生成command參數,同時添加一個返回值參數   
  118.   
  119.          GenerateCommandParameters(command, method, values);   
  120.   
  121.          command.Parameters.Add(ReturnValueParameterName, SqlDbType.Int).Direction    
  122.   
  123.                               =ParameterDirection.ReturnValue;   
  124.   
  125.   
  126.   
  127.          return command;   
  128.   
  129.       }   
  130.   
  131.   
  132.   
  133.       private static void GenerateCommandParameters(   
  134.   
  135.                            SqlCommand command, MethodInfo method, object[] values)   
  136.   
  137.       {   
  138.   
  139.   
  140.   
  141.          // 得到所有的參數,通過循環一一進行處理。   
  142.   
  143.             
  144.   
  145.          ParameterInfo[] methodParameters = method.GetParameters();   
  146.   
  147.          int paramIndex = 0;   
  148.   
  149.   
  150.   
  151.          foreach (ParameterInfo paramInfo in methodParameters)   
  152.   
  153.          {   
  154.   
  155.             // 忽略掉參數被標記爲[NonCommandParameter ]的參數   
  156.   
  157.             
  158.   
  159.             if (Attribute.IsDefined(paramInfo, typeof(NonCommandParameterAttribute)))   
  160.   
  161.                continue;   
  162.   
  163.                
  164.   
  165.             // 獲取參數的SqlParameter attribute,如果沒有指定,那麼就創建一個並使用它的缺省設置。   
  166.   
  167.             SqlParameterAttribute paramAttribute = (SqlParameterAttribute) Attribute.GetCustomAttribute(   
  168.   
  169.      paramInfo, typeof(SqlParameterAttribute));   
  170.   
  171.       
  172.   
  173.       if (paramAttribute == null)   
  174.   
  175.          paramAttribute = new SqlParameterAttribute();   
  176.   
  177.          
  178.   
  179.       //使用attribute的設置來配置一個參數對象。使用那些已經定義的參數值。如果沒有定義,那麼就從方法    
  180.   
  181.       // 的參數來推斷它的參數值。   
  182.   
  183.       SqlParameter sqlParameter = new SqlParameter();   
  184.   
  185.       if (paramAttribute.IsNameDefined)   
  186.   
  187.          sqlParameter.ParameterName = paramAttribute.Name;   
  188.   
  189.       else  
  190.   
  191.          sqlParameter.ParameterName = paramInfo.Name;   
  192.   
  193.   
  194.   
  195.             if (!sqlParameter.ParameterName.StartsWith("@"))   
  196.   
  197.                sqlParameter.ParameterName = "@" + sqlParameter.ParameterName;   
  198.   
  199.             
  200.   
  201.             if (paramAttribute.IsTypeDefined)   
  202.   
  203.                sqlParameter.SqlDbType = paramAttribute.SqlDbType;   
  204.   
  205.                
  206.   
  207.             if (paramAttribute.IsSizeDefined)   
  208.   
  209.                sqlParameter.Size = paramAttribute.Size;   
  210.   
  211.   
  212.   
  213.             if (paramAttribute.IsScaleDefined)   
  214.   
  215.                sqlParameter.Scale = paramAttribute.Scale;   
  216.   
  217.                
  218.   
  219.             if (paramAttribute.IsPrecisionDefined)   
  220.   
  221.                sqlParameter.Precision = paramAttribute.Precision;   
  222.   
  223.                
  224.   
  225.             if (paramAttribute.IsDirectionDefined)   
  226.   
  227.             {   
  228.   
  229.                sqlParameter.Direction = paramAttribute.Direction;   
  230.   
  231.             }   
  232.   
  233.             else  
  234.   
  235.             {   
  236.   
  237.                if (paramInfo.ParameterType.IsByRef)   
  238.   
  239.                {   
  240.   
  241.                   sqlParameter.Direction = paramInfo.IsOut ?    
  242.   
  243.                               ParameterDirection.Output :    
  244.   
  245.                               ParameterDirection.InputOutput;   
  246.   
  247.                }   
  248.   
  249.                else  
  250.   
  251.                {   
  252.   
  253.                   sqlParameter.Direction = ParameterDirection.Input;   
  254.   
  255.                }   
  256.   
  257.             }   
  258.   
  259.             
  260.   
  261.             // 檢測是否提供的足夠的參數對象值   
  262.   
  263.      Debug.Assert(paramIndex < values.Length);   
  264.   
  265.               
  266.   
  267.            //把相應的對象值賦於參數。   
  268.   
  269.            sqlParameter.Value = values[paramIndex];   
  270.   
  271.            command.Parameters.Add(sqlParameter);   
  272.   
  273.                      
  274.   
  275.                      
  276.   
  277.            paramIndex++;   
  278.   
  279.          }   
  280.   
  281.          
  282.   
  283.          //檢測是否有多餘的參數對象值   
  284.   
  285.          Debug.Assert(paramIndex == values.Length);   
  286.   
  287.       }   
  288.   
  289.    }   
  290.   
  291. }  
//SqlCommandGenerator.cs



using System;

using System.Reflection;

using System.Data;

using System.Data.SqlClient;

using Debug = System.Diagnostics.Debug;

using StackTrace = System.Diagnostics.StackTrace;  



類代碼: 

namespace DataAccess

{

   public sealed class SqlCommandGenerator

   {

      //私有構造器,不允許使用無參數的構造器構造一個實例

      private SqlCommandGenerator()

      {

         throw new NotSupportedException();

      }



      //靜態只讀字段,定義用於返回值的參數名稱

      public static readonly string ReturnValueParameterName = "RETURN_VALUE";

      //靜態只讀字段,用於不帶參數的存儲過程

      public static readonly object[] NoValues = new object[] {};

   

      

      public static SqlCommand GenerateCommand(SqlConnection connection,

                                  MethodInfo method, object[] values)

      {

         //如果沒有指定方法名稱,從堆棧幀得到方法名稱

         if (method == null)

             method = (MethodInfo) (new StackTrace().GetFrame(1).GetMethod());



         // 獲取方法傳進來的SqlCommandMethodAttribute

         // 爲了使用該方法來生成一個Command對象,要求有這個Attribute。

         SqlCommandMethodAttribute commandAttribute = 

            (SqlCommandMethodAttribute) Attribute.GetCustomAttribute(method, typeof(SqlCommandMethodAttribute));



         Debug.Assert(commandAttribute != null);

         Debug.Assert(commandAttribute.CommandType == CommandType.StoredProcedure ||

       commandAttribute.CommandType == CommandType.Text);



         // 創建一個SqlCommand對象,同時通過指定的attribute對它進行配置。

         SqlCommand command = new SqlCommand();

         command.Connection = connection;

         command.CommandType = commandAttribute.CommandType;

      

         // 獲取command的文本,如果沒有指定,那麼使用方法的名稱作爲存儲過程名稱 

         if (commandAttribute.CommandText.Length == 0)

         {

            Debug.Assert(commandAttribute.CommandType == CommandType.StoredProcedure);

            command.CommandText = method.Name;

         }

         else

         {

            command.CommandText = commandAttribute.CommandText;

         }



         // 調用GeneratorCommandParameters方法,生成command參數,同時添加一個返回值參數

         GenerateCommandParameters(command, method, values);

         command.Parameters.Add(ReturnValueParameterName, SqlDbType.Int).Direction 

                              =ParameterDirection.ReturnValue;



         return command;

      }



      private static void GenerateCommandParameters(

                           SqlCommand command, MethodInfo method, object[] values)

      {



         // 得到所有的參數,通過循環一一進行處理。

         

         ParameterInfo[] methodParameters = method.GetParameters();

         int paramIndex = 0;



         foreach (ParameterInfo paramInfo in methodParameters)

         {

            // 忽略掉參數被標記爲[NonCommandParameter ]的參數

         

            if (Attribute.IsDefined(paramInfo, typeof(NonCommandParameterAttribute)))

               continue;

            

            // 獲取參數的SqlParameter attribute,如果沒有指定,那麼就創建一個並使用它的缺省設置。

            SqlParameterAttribute paramAttribute = (SqlParameterAttribute) Attribute.GetCustomAttribute(

     paramInfo, typeof(SqlParameterAttribute));

   

      if (paramAttribute == null)

         paramAttribute = new SqlParameterAttribute();

      

      //使用attribute的設置來配置一個參數對象。使用那些已經定義的參數值。如果沒有定義,那麼就從方法 

      // 的參數來推斷它的參數值。

      SqlParameter sqlParameter = new SqlParameter();

      if (paramAttribute.IsNameDefined)

         sqlParameter.ParameterName = paramAttribute.Name;

      else

         sqlParameter.ParameterName = paramInfo.Name;



            if (!sqlParameter.ParameterName.StartsWith("@"))

               sqlParameter.ParameterName = "@" + sqlParameter.ParameterName;

         

            if (paramAttribute.IsTypeDefined)

               sqlParameter.SqlDbType = paramAttribute.SqlDbType;

            

            if (paramAttribute.IsSizeDefined)

               sqlParameter.Size = paramAttribute.Size;



            if (paramAttribute.IsScaleDefined)

               sqlParameter.Scale = paramAttribute.Scale;

            

            if (paramAttribute.IsPrecisionDefined)

               sqlParameter.Precision = paramAttribute.Precision;

            

            if (paramAttribute.IsDirectionDefined)

            {

               sqlParameter.Direction = paramAttribute.Direction;

            }

            else

            {

               if (paramInfo.ParameterType.IsByRef)

               {

                  sqlParameter.Direction = paramInfo.IsOut ? 

                              ParameterDirection.Output : 

                              ParameterDirection.InputOutput;

               }

               else

               {

                  sqlParameter.Direction = ParameterDirection.Input;

               }

            }

         

            // 檢測是否提供的足夠的參數對象值

     Debug.Assert(paramIndex < values.Length);

           

           //把相應的對象值賦於參數。

           sqlParameter.Value = values[paramIndex];

           command.Parameters.Add(sqlParameter);

                  

                  

           paramIndex++;

         }

      

         //檢測是否有多餘的參數對象值

         Debug.Assert(paramIndex == values.Length);

      }

   }

}

必要的工作終於完成了。SqlCommandGenerator中的代碼都加上了註釋,所以並不難讀懂。下面我們進入最後的一步,那就是使用新的方法來實現上一節我們一開始顯示個那個AddCustomer的方法。

重構新的AddCustomer代碼:

  1. SqlCommandMethod(CommandType.StoredProcedure) ]   
  2.   
  3. public void AddCustomer( [NonCommandParameter] SqlConnection connection,    
  4.   
  5.                    [SqlParameter(50)] string customerName,    
  6.   
  7.                    [SqlParameter(20)] string country,    
  8.   
  9.                    [SqlParameter(20)] string province,    
  10.   
  11.                    [SqlParameter(20)] string city,    
  12.   
  13.                    [SqlParameter(60)] string address,    
  14.   
  15.                    [SqlParameter(16)] string telephone,   
  16.   
  17.                    out int customerId )   
  18.   
  19. {   
  20.   
  21.    customerId=0; //需要初始化輸出參數   
  22.   
  23.   //調用Command生成器生成SqlCommand實例   
  24.   
  25.    SqlCommand command = SqlCommandGenerator.GenerateCommand( connection, nullnew object[]   
  26.   
  27. {customerName,country,province,city,address,telephone,customerId } );   
  28.   
  29.                             
  30.   
  31.    connection.Open();   
  32.   
  33.    command.ExecuteNonQuery();   
  34.   
  35.    connection.Close();   
  36.   
  37.   
  38.   
  39.    //必須明確返回輸出參數的值   
  40.   
  41.    customerId=(int)command.Parameters["@CustomerId"].Value;   
  42.   
  43. }  
SqlCommandMethod(CommandType.StoredProcedure) ]

public void AddCustomer( [NonCommandParameter] SqlConnection connection, 

                   [SqlParameter(50)] string customerName, 

                   [SqlParameter(20)] string country, 

                   [SqlParameter(20)] string province, 

                   [SqlParameter(20)] string city, 

                   [SqlParameter(60)] string address, 

                   [SqlParameter(16)] string telephone,

                   out int customerId )

{

   customerId=0; //需要初始化輸出參數

  //調用Command生成器生成SqlCommand實例

   SqlCommand command = SqlCommandGenerator.GenerateCommand( connection, null, new object[]

{customerName,country,province,city,address,telephone,customerId } );

                         

   connection.Open();

   command.ExecuteNonQuery();

   connection.Close();



   //必須明確返回輸出參數的值

   customerId=(int)command.Parameters["@CustomerId"].Value;

}

代碼中必須注意的就是out參數,需要事先進行初始化,並在Command執行操作以後,把參數值傳回給它。受益於Attribute,使我們擺脫了那種編寫大量枯燥代碼編程生涯。 我們甚至還可以使用Sql存儲過程來編寫生成整個方法的代碼,如果那樣做的話,可就大大節省了你的時間了,上一節和這一節中所示的代碼,你可以把它們單獨編譯成一個組件,這樣就可以在你的項目中不斷的重用它們了。從下一節開始,我們將更深層次的介紹Attribute的應用,請繼續關注。(待續)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章