zoukankan      html  css  js  c++  java
  • .NET 特性Attribute[三]

         刚刚接触Attribute的朋友可能很难想明白Attribute究竟有何用处,以及在应用程序中我们如何使用Attribute。在这节,通过一个例子来演示Attribute的用处!

         针对数据库的操作通常是新手入门时经常接触的例子。在应用程序中,我们经常会遇见以下代码

            public int AddCustomer(SqlConnection connection,string customerName,string country,string province, string city,             string address,string telephone)        
            {
                SqlCommand command
    =new SqlCommand("AddCustomer", connection);
                command.CommandType
    =CommandType.StoredProcedure;

                command.Parameters.Add(
    "@CustomerName",SqlDbType.NVarChar,50).Value=customerName;
                command.Parameters.Add(
    "@country",SqlDbType.NVarChar,20).Value=country;
                command.Parameters.Add(
    "@Province",SqlDbType.NVarChar,20).Value=province;
                command.Parameters.Add(
    "@City",SqlDbType.NVarChar,20).Value=city;
                command.Parameters.Add(
    "@Address",SqlDbType.NVarChar,60).Value=address;
                command.Parameters.Add(
    "@Telephone",SqlDbType.NvarChar,16).Value=telephone;
                command.Parameters.Add(
    "@CustomerId",SqlDbType.Int,4).Direction=ParameterDirection.Output;

                connection.Open();
                command.ExecuteNonQuery();
                connection.Close();

                
    int custId=(int)command.Parameters["@CustomerId"].Value;
                
    return custId;
            }    

    上面的代码,创建一个Command实例,然后添加存储过程的参数,然后调用ExecuteMonQuery方法执行数据的插入操作,最后返回CustomerId。从代码可以看到参数的添加command.Parameters.Add()是一种重复单调的工作。而往往一个项目中,经常有一百多个甚至成千的存储过程,每次编写这样的代码是否会感到枯燥无味?作为开发人员的你,是否会想偷偷懒?

    当然,现在喜庆的是出现了非常多的Coder,代码生成器的出现无疑可以解决上面的问题,但在这里,从技术交流的角度,给出一个另类的作法。就是使用Attribute。

    在开始之前先理清我们的思路。我们是为了根据方法的参数及方法的名称,自动的给我们“加工”成一个Command对象。而不需要我们自己手工一个个的Add进去。

    第1步:创建一个SqlParameterAttribute类

    SqlParameterAttribute.cs

    这个类很简单,就普通的一个对象类差不多,就拥有一些属性。但不同的是他继承了Attribute。这样也就是说,这个类可以被当作特性来使用!注意类头的一行[ AttributeUsage(AttributeTargets.Parameter) ]表明该特性,只可作用在方法的参数上!

    第2步:考虑到方法中并不是每个参数都是存储过程需要的。所以我们可以再定义一个Attribute来标识这样的参数。

        [ AttributeUsage(AttributeTargets.Parameter) ]
        
    public sealed class NonCommandParameterAttribute : Attribute
        {
            
    //仅作为标识使用 用于方法参数中,并不是执行SQL所需要的参数
        }

    第3步:到此我们已经完成了SQL的参数Attribute的定义,在创建Command对象生成器之前,让我们考虑这样的一个事实,那就是如果我们数据访问层调用的不是存储过程,也就是说Command的CommandType不是存储过程,而是带有参数的SQL语句,我们想让我们的方法一样可以适合这种情况,同样我们仍然可以使用Attribute,定义一个用于方法的Attribute来表明该方法中的生成的Command的CommandType是存储过程还是SQL文本,并且我们同样希望以相对简单的方式来定义存储过程的名称。下面是新定义的Attribute的代码:

    [AttributeUsage(AttributeTargets.Method)]
        
    public sealed class SqlCommandMethodAttribute : Attribute
        {
            
    private string commandText;            //SQL执行文本
            private CommandType commandType;    //SQL执行类型

            
    public SqlCommandMethodAttribute( CommandType commandType, string commandText)
            {
                
    this.commandType=commandType;
                
    this.commandText=commandText;
            }

            
    public SqlCommandMethodAttribute(CommandType commandType) : this(commandType, null){}

            
    public string CommandText
            {
                
    get
                {
                    
    return commandText==null ? string.Empty : commandText;
                }
                
    set
                {
                    commandText
    =value;
                }
            }

            
            
    public CommandType CommandType
            {
                
    get
                {
                    
    return commandType;
                }
                
    set
                {
                    commandType
    =value;
                }
            }
        }

    第4步:SqlCommandGenerator类的设计

    SqlCommandGEnerator类的设计思路就是通过反射得到方法的参数,使用被SqlCommandParameterAttribute标记的参数来装配一个Command实例。

    using System;
    using System.Reflection;
    using System.Data;
    using System.Data.SqlClient;
    using Debug = System.Diagnostics.Debug;
    using StackTrace = System.Diagnostics.StackTrace;  


    namespace AttributeDemo
    {
        
    /// <summary>
        
    /// SqlCommandGenerator 的摘要说明。
        
    /// </summary>
        public 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);
            }

        }
    }

    好了,一切就是如此。现在我们就可以使用这些特性来修改在文章开始给出的示例!

    [SqlCommandMethodAttribute(CommandType.StoredProcedure)]
            
    public void UP_Customer_ADD( [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, nullnew object[]{customerName,country,province,city,address,telephone,customerId } );
                             
                connection.Open();
                command.ExecuteNonQuery();
                connection.Close();

                
    //必须明确返回输出参数的值
                customerId=(int)command.Parameters["@CustomerId"].Value;
            }

    消除了前面所看到的command.Parameters.Add方式。注意方法的头定义的 [SqlCommandMethodAttribute(CommandType.StoredProcedure)]特性,表明该方法执行的是存储过程。那么到这里可能有朋友问,那过程的名称是什么呢,细心看GenerateCommand方法的朋友,可以知道,当没有传入存储过程名称时,会自动以方法的名称作为存储过程的名称,这里是“UP_Customer_ADD” 如果要明确写出存储过程名称,则将方法头改为 [SqlCommandMethodAttribute(CommandType.StoredProcedure,"sp_yourProc")],则sp_yourProc即为将要执行的存储过程名称!

    为讲解的完整性下面给出程序的调用及SQL表及存储过程

    [TestAttribute("ddm")]
            
    private void btnSave_Click(object sender, System.EventArgs e)
            {


                
    int addID = 0;

                SqlConnection con 
    = new SqlConnection("server=.;database=fyDemo;uid=sa;pwd=sa;");

                UP_Customer_ADD(con,
    "姓名","国家","湖北省","武汉","地址","133333333333",out addID);

        
                MessageBox.Show(
    "添加成功:ID为"+addID.ToString());
            }
    sqlscript.sql

     

  • 相关阅读:
    Python·Jupyter Notebook
    CNN(卷积神经网络)、RNN(循环神经网络)、DNN(深度神经网络)概念区分理解
    tensorflow学习
    语料库
    资源 | 数十种TensorFlow实现案例汇集:代码+笔记
    Tensorlayer
    利用 TFLearn 快速搭建经典深度学习模型
    十分钟搞定pandas
    利用python进行数据分析之pandas入门
    Pandas
  • 原文地址:https://www.cnblogs.com/eflylab/p/1292894.html
Copyright © 2011-2022 走看看