在以前ado.net时候,我们使用存储过程返回一个列表,可以将结果集放在DataTable中,如果我们需要将结果集放在一个强类型集合(如List<T>)中我们该怎么做呢?之前在网上看到过一种解决方法,忘记出处了,请谅解。大概思路是: 在用DataReader读取一行记录时,将该行创建为一个对象,然后添加到列表中 。
我在EF3.5中使用存储过程,需要在edmx(领域模型中)文件中做函数导入(Function Import),并且返回值类型必须是数据库中已存在的实体。
这样做的缺憾(不够灵活)如下:
1、必须要函数导入,如果后来修改或更新实体模型,要维护该函数。(可以接受)
2、存储过程返回的对象必须是数据库已存在的实体,不然无法选择返回值类型。(目前没找到合理方法,要在数据库中建一个空表,无法接受,但在EF4.0中返回类型哪里可以新建ComplexType)。
所以在项目中少许的存储过程就用ado.net了。现有一个示例场景,从northwind的Products表中,获取一部分数据,得到产品编号,产品名称,单价这三列。那么存储过程中的sql语句:select ProductID,ProductName,UnitPrice from Products。
第一个版本的实现:
1、返回的数据实体如:
 View Code
    
    
      View Code 
    
    
        
           1
        
        
          public
        
        
          class
        
         MyModel
        
        
           2
        
             {
        
        
           3
        
        
          public
        
        
          int
        
         ProductId { 
        
          get
        
        ; 
        
          set
        
        ; }
        
        
           4
        
        
          public
        
        
          string
        
         ProductName { 
        
          get
        
        ; 
        
          set
        
        ; }
        
        
           5
        
        
          public
        
        
          decimal
        
         UnitPrice { 
        
          get
        
        ; 
        
          set
        
        ; }
        
        
           6
        
        
        
           7
        
        
          ///
        
        
          <summary>
        
        
          
        
        
           8
        
        
          ///
        
        
           创建该对象
          
        
        
           9
        
        
          ///
        
        
          </summary>
        
        
          
        
        
          10
        
        
          ///
        
        
          <param name="record"></param>
        
        
          
        
        
          11
        
        
          ///
        
        
          <returns></returns>
        
        
          
        
        
          12
        
        
          public
        
        
          static
        
         MyModel Create(IDataRecord record)
        
        
          13
        
                 {
        
        
          14
        
        
          return
        
        
          new
        
         MyModel()
        
        
          15
        
                     {
        
        
          16
        
                         ProductId = Field<
        
          int
        
        >(record, 
        
          "
        
        
          ProductID
        
        
          "
        
        ),
        
        
          17
        
                         ProductName = Field<
        
          string
        
        >(record, 
        
          "
        
        
          ProductName
        
        
          "
        
        ),
        
        
          18
        
                         UnitPrice = Field<
        
          decimal
        
        >(record, 
        
          "
        
        
          UnitPrice
        
        
          "
        
        )
        
        
          19
        
                     };
        
        
          20
        
                 }
        
        
          21
        
        
          ///
        
        
          <summary>
        
        
          
        
        
          22
        
        
          ///
        
        
           获取某个字段的值
          
        
        
          23
        
        
          ///
        
        
          </summary>
        
        
          
        
        
          24
        
        
          ///
        
        
          <typeparam name="T"></typeparam>
        
        
          
        
        
          25
        
        
          ///
        
        
          <param name="record"></param>
        
        
          
        
        
          26
        
        
          ///
        
        
          <param name="fieldName"></param>
        
        
          
        
        
          27
        
        
          ///
        
        
          <returns></returns>
        
        
          
        
        
          28
        
        
          public
        
        
          static
        
         T Field<T>(IDataRecord record, 
        
          string
        
         fieldName)
        
        
          29
        
                 {
        
        
          30
        
                     T fieldValue = 
        
          default
        
        (T);
        
        
          31
        
        
        
          32
        
        
          if
        
         (record[fieldName] != DBNull.Value)
        
        
          33
        
                     {
        
        
          34
        
                         fieldValue = (T)record[fieldName];
        
        
          35
        
                     }
        
        
          36
        
        
        
          37
        
        
          return
        
         fieldValue;
        
        
          38
        
                 }
        
        
          39
        
             }
      
    2、在数据访问层(DAL)中,编写一个泛型方法。如:
 View Code
    
    
      View Code 
    
    
        
           1
        
        
          ///
        
        
          <summary>
        
        
          
        
        
           2
        
        
          ///
        
        
           执行存储过程得到集合列表
          
        
        
           3
        
        
          ///
        
        
          </summary>
        
        
          
        
        
           4
        
        
          ///
        
        
          <typeparam name="TResult"></typeparam>
        
        
          
        
        
           5
        
        
          ///
        
        
          <param name="procedureName">
        
        
          存储过程名称
        
        
          </param>
        
        
          
        
        
           6
        
        
          ///
        
        
          <param name="creater">
        
        
          创建对象的委托
        
        
          </param>
        
        
          
        
        
           7
        
        
          ///
        
        
          <param name="parameters"></param>
        
        
          
        
        
           8
        
        
          ///
        
        
          <returns></returns>
        
        
          
        
        
           9
        
        
          public
        
         List<TResult> ExecuteProcedureList<TResult>(
        
          string
        
         procedureName,Func<IDataRecord,TResult> creater, 
        
          params
        
         SqlParameter[] parameters)
        
        
          10
        
                 {
        
        
          11
        
                     List<TResult> result = 
        
          new
        
         List<TResult>();
        
        
          12
        
        
        
          13
        
        
          //
        
        
          get sqlconnection string
        
        
          
        
        
          14
        
        
          string
        
         connString = (ObjectContext.Connection 
        
          as
        
         EntityConnection).StoreConnection.ConnectionString;
        
        
          15
        
        
        
          16
        
                     SqlConnection conn = 
        
          new
        
         SqlConnection(connString);
        
        
          17
        
                     SqlCommand cmd = conn.CreateCommand();
        
        
          18
        
                     cmd.CommandType = CommandType.StoredProcedure;
        
        
          19
        
                     cmd.Parameters.AddRange(parameters);
        
        
          20
        
                     cmd.CommandText = procedureName;
        
        
          21
        
        
        
          22
        
        
          try
        
        
        
          23
        
                     {
        
        
          24
        
        
          if
        
         (conn.State != ConnectionState.Open)
        
        
          25
        
                         {
        
        
          26
        
                             conn.Open();
        
        
          27
        
                         }
        
        
          28
        
        
          using
        
         (DbDataReader reader = cmd.ExecuteReader())
        
        
          29
        
                         {
        
        
          30
        
        
          while
        
         (reader.Read())
        
        
          31
        
                             {
        
        
          32
        
                                 TResult model = creater(reader);
        
        
          33
        
                                 result.Add(model);
        
        
          34
        
                             }
        
        
          35
        
                         }
        
        
          36
        
                     }
        
        
          37
        
        
          catch
        
         { 
        
          throw
        
        ; }
        
        
          38
        
        
          finally
        
        
        
          39
        
                     {
        
        
          40
        
                         cmd.Dispose();
        
        
          41
        
                         conn.Close();
        
        
          42
        
                     }
        
        
          43
        
        
          return
        
         result;
        
        
          44
        
                 }
      
    3、调用代码:
      
        List<MyModel>
      
       resultList=  bll.ExecuteProcedureList<MyModel>(
      
        "
      
      
        proc_getlist
      
      
        "
      
      , MyModel.Create);
    
  上面的代码可能存在不足,比如:
1、MyModel中的Create方法是每个model必须自己实现(是否可以约束类型必须存在Create方法)。
2、MyModel中的Field泛型方法是用来获取字段的值,它的功能独立于该Model,可以抽离出来,写在其他地方。
3、调用时候还必须传递Model中的创建对象的方法 等,是否我们可以进一步的封装呢?
我的设计(第二版)是: 将Field方法和Create的声明写在一个抽象基类中,Field方法作为工具使用static,Create作为抽象方法,让实现者必须实现类型的创建。
      
         1
      
      
        ///
      
      
        <summary>
      
      
        
      
      
         2
      
      
        ///
      
      
         作为 EF中使用存储过程返回集合 对象的基类
        
      
      
         3
      
      
        ///
      
      
        </summary>
      
      
        
      
      
         4
      
      
        ///
      
      
        <typeparam name="TResult"></typeparam>
      
      
        
      
      
         5
      
      
        public
      
      
        abstract
      
      
        class
      
       ProcedureModel
      
      
         6
      
           {
      
      
         7
      
      
        ///
      
      
        <summary>
      
      
        
      
      
         8
      
      
        ///
      
      
         获取record中字段的值,如果不存在则返回默认值
        
      
      
         9
      
      
        ///
      
      
        </summary>
      
      
        
      
      
        10
      
      
        ///
      
      
        <typeparam name="T"></typeparam>
      
      
        
      
      
        11
      
      
        ///
      
      
        <param name="record"></param>
      
      
        
      
      
        12
      
      
        ///
      
      
        <param name="fieldName"></param>
      
      
        
      
      
        13
      
      
        ///
      
      
        <returns></returns>
      
      
        
      
      
        14
      
      
        public
      
      
        static
      
       T Field<T>(IDataRecord record, 
      
        string
      
       fieldName)
      
      
        15
      
               {
      
      
        16
      
                   T fieldValue = 
      
        default
      
      (T);
      
      
        17
      
      
      
        18
      
      
        if
      
       (record[fieldName] != DBNull.Value)
      
      
        19
      
                   {
      
      
        20
      
                       fieldValue = (T)record[fieldName];
      
      
        21
      
                   }
      
      
        22
      
      
      
        23
      
      
        return
      
       fieldValue;
      
      
        24
      
               }
      
      
        25
      
      
      
        26
      
      
        ///
      
      
        <summary>
      
      
        
      
      
        27
      
      
        ///
      
      
         创建该对象
        
      
      
        28
      
      
        ///
      
      
        </summary>
      
      
        
      
      
        29
      
      
        ///
      
      
        <param name="record"></param>
      
      
        
      
      
        30
      
      
        ///
      
      
        <returns></returns>
      
      
        
      
      
        31
      
      
        public
      
      
        abstract
      
       ProcedureModel Create(IDataRecord record);
      
      
        32
      
           }
    
  
      
         1
      
      
        public
      
      
        class
      
       MyModel : ProcedureModel
      
      
         2
      
           {
      
      
         3
      
      
        public
      
      
        int
      
       ProductId { 
      
        get
      
      ; 
      
        set
      
      ; }
      
      
         4
      
      
        public
      
      
        string
      
       ProductName { 
      
        get
      
      ; 
      
        set
      
      ; }
      
      
         5
      
      
        public
      
      
        decimal
      
       UnitPrice { 
      
        get
      
      ; 
      
        set
      
      ; }
      
      
         6
      
      
      
         7
      
      
      
         8
      
      
      
         9
      
      
      
        10
      
      
        #region
      
       创建实体的方法
      
      
        11
      
      
      
        12
      
      
        public
      
      
        override
      
       ProcedureModel Create(IDataRecord record)
      
      
        13
      
               {
      
      
        14
      
      
        return
      
      
        new
      
       MyModel()
      
      
        15
      
                   {
      
      
        16
      
                       ProductId = Field<
      
        int
      
      >(record, 
      
        "
      
      
        ProductID
      
      
        "
      
      ),
      
      
        17
      
                       ProductName = Field<
      
        string
      
      >(record, 
      
        "
      
      
        ProductName
      
      
        "
      
      ),
      
      
        18
      
                       UnitPrice = Field<
      
        string
      
      >(record, 
      
        "
      
      
        UnitPrice
      
      
        "
      
      )
      
      
        19
      
                   };
      
      
        20
      
               }
      
      
        21
      
      
      
        22
      
      
        #endregion
      
      
      
        23
      
           }
    
  数据访问层中:
      
         1
      
      
        ///
      
      
        <summary>
      
      
        
      
      
         2
      
      
        ///
      
      
         执行存储过程返回集合
        
      
      
         3
      
      
        ///
      
      
        </summary>
      
      
        
      
      
         4
      
      
        ///
      
      
        <typeparam name="TResult">
      
      
        实体类型
      
      
        </typeparam>
      
      
        
      
      
         5
      
      
        ///
      
      
        <param name="procedureName">
      
      
        储存过程名称
      
      
        </param>
      
      
        
      
      
         6
      
      
        ///
      
      
        <param name="createTResult">
      
      
        创建实体的方法
      
      
        </param>
      
      
        
      
      
         7
      
      
        ///
      
      
        <param name="parameters">
      
      
        存储过程的参数
      
      
        </param>
      
      
        
      
      
         8
      
      
        ///
      
      
        <returns></returns>
      
      
        
      
      
         9
      
      
        public
      
       List<TResult> ExecuteProcedureList<TResult>(
      
        string
      
       procedureName, 
      
        params
      
       SqlParameter[] parameters)
      
      
        10
      
      
        where
      
       TResult : ProcedureModel,
      
        new
      
      ()
      
      
        11
      
               {
      
      
        12
      
                   List<TResult> result = 
      
        new
      
       List<TResult>();
      
      
        13
      
      
      
        14
      
      
        //
      
      
        get sqlconnection string
      
      
        
      
      
        15
      
      
        string
      
       connString = (ObjectContext.Connection 
      
        as
      
       EntityConnection).StoreConnection.ConnectionString;
      
      
        16
      
      
      
        17
      
                   SqlConnection conn = 
      
        new
      
       SqlConnection(connString);
      
      
        18
      
                   SqlCommand cmd = conn.CreateCommand();
      
      
        19
      
                   cmd.CommandType = CommandType.StoredProcedure;
      
      
        20
      
                   cmd.Parameters.AddRange(parameters);
      
      
        21
      
                   cmd.CommandText = procedureName;
      
      
        22
      
      
      
        23
      
      
      
        24
      
      
        try
      
      
      
        25
      
                   {
      
      
        26
      
      
        if
      
       (conn.State != ConnectionState.Open)
      
      
        27
      
                       {
      
      
        28
      
                           conn.Open();
      
      
        29
      
                       }
      
      
        30
      
      
      
        31
      
                       Func<IDataRecord, ProcedureModel> creater = (
      
        new
      
       TResult() 
      
        as
      
       ProcedureModel).Create;
      
      
        32
      
      
      
        33
      
      
        using
      
       (DbDataReader reader = cmd.ExecuteReader())
      
      
        34
      
                       {
      
      
        35
      
      
        while
      
       (reader.Read())
      
      
        36
      
                           {
      
      
        37
      
                               TResult model = (TResult)creater(reader);
      
      
        38
      
                               result.Add(model);
      
      
        39
      
                           }
      
      
        40
      
                       }
      
      
        41
      
                   }
      
      
        42
      
      
        catch
      
       { 
      
        throw
      
      ; }
      
      
        43
      
      
        finally
      
      
      
        44
      
                   {
      
      
        45
      
                       cmd.Dispose();
      
      
        46
      
                       conn.Close();
      
      
        47
      
                   }
      
      
        48
      
      
        return
      
       result;
      
      
        49
      
               }
    
  调用:
    
      List<MyModel>
    
     resultList=  bll.ExecuteProcedureList<MyModel>(
    
      "
    
    
      proc_getlist
    
    
      "
    
    );
  
  
     
  
  通过以上可以看出,在第二个版本中,调用时去掉了对象创建方法的传递,如果其他开发者使用ExecuteProcedureList,则强制继承ProdureModel抽象类,这样的约束。
当然上面存储过程没有做分页(数据量大的情况,会影响性能),根据需要可以自己实现。第二个版本的实现请在需要的地方合理使用。
对于第二个实现,我有一些问题想请教高手们。
1、对于创建对象的方法Create,我觉得实现为static比较好,这样每个实例都可以共用一个Create方法,无需每个对象都有Create方法,如何做?
2、对于Create方法,返回值问题。现在是返回的基类型,会设计到类型转换的问题,是否有性能影响(这里不涉及装箱拆箱)。如果返回具体类型,该如何做?
3、对于在DAL的ExecuteProcedureList中,如何获取创建对象的方法问题。目前是创建一个对象,通过多肽得到子类的实现方法,如果static如何做,(反射)?
4、总之,有没有更好的设计呢?期待着大牛的指点,先谢谢啦
上面算是抛砖引玉吧,期待大家的回复和讨论,我们共同进步,谢谢大家!


 
     
					 
					