C# 为字段填充返回SQL数据行的正确方法

C# 为字段填充返回SQL数据行的正确方法,c#,sql,C#,Sql,我使用以下场景从SQL数据库返回一行。返回数据后,我只是用它填充web表单上的字段 示例数据库类: public class DBAccess { public DataTable ReturnContactInfo(int userID) { // executes a stored procedure, populates a datatable, then returns it } } 使用类填充理论客户名称字段的示例: protected voi

我使用以下场景从SQL数据库返回一行。返回数据后,我只是用它填充web表单上的字段

示例数据库类:

public class DBAccess
{
    public DataTable ReturnContactInfo(int userID)
    {
        // executes a stored procedure, populates a datatable, then returns it
    }
}
使用类填充理论客户名称字段的示例:

protected void Page_Load(object sender, EventArgs e)
{
    if(!isPostBack)
    {
        DBAccess db = new DBAccess();
        DataTable dt =  db.ReturnContactInfo(1);
        if (dt.Rows.Count > 0)
        {
           customerName.Text = Convert.ToString(dt.Rows[0]["customerName"]);
        }
     }
}

有没有更好或更有效的方法来实现这一点,即返回单个数据行以从中提取数据?这种方法当然有效,但是当我总是并且只处理索引0处的行时,必须键入dt.Rows[0][string]有点麻烦。请记住,我希望能够将所有数据层内容提取到单个类中。据我所知,这意味着我不能使用SqlDataReader,因为它在完成时必须关闭,因此我无法从数据层类返回它。

如果知道只返回1行,为什么不返回DataRow而不是DataTable?

如果知道只返回1行,为什么不返回DataRow而不是DataTable行?

定义一个类,其中包含您想要作为属性的成员。然后填充并返回该对象。您可以使用dapper之类的工具简化此过程:

public Customer GetCustomer(int id)
{
     return connection.Query<Customer>("getCustomer",
         new {id}, // <=== param
         commandType: CommandType.StoredProcedure).Single();
}
现在:

很明显,我们只有一个目标 对象上的成员是明显的井类型 您避免了DataTable的大量开销
定义一个类,其中包含要作为属性的成员。然后填充并返回该对象。您可以使用dapper之类的工具简化此过程:

public Customer GetCustomer(int id)
{
     return connection.Query<Customer>("getCustomer",
         new {id}, // <=== param
         commandType: CommandType.StoredProcedure).Single();
}
现在:

很明显,我们只有一个目标 对象上的成员是明显的井类型 您避免了DataTable的大量开销
我编写了一个代码生成器,生成数据访问代码,每个存储过程一个类。其中一个选项是,您可以指定其结果集由一行组成。用法很简单:生成的代码通过包装器方法调用,包装器方法强制执行用法契约所需的任何约束,因此:

    public DataRow GetPatientData( int patientID )
    {
        dbo_GetPatientData67 sp = new dbo_GetPatientData( CONNECT_STRING_ID ) ;
        int                  rc = sp.Exec( patientID ) ;
        DataRow              dr = sp.ResultSet ;

        if ( dr == null ) throw new InvalidOperationException("nothing returned from stored procedure.") ;
        return dr ;
    }
以下是数据访问代码的要点:

/* generated code. do not change -- generated code */
public class dbo_GetPatientData
{

  public int     ReturnCode { get ; private set ; }
  public DataRow ResultSet { get ; private set ; }

  public int Exec(  int? @iPatientID )
  {
    using ( SqlConnection  conn = new SqlConnection( this._connectString ) )
    using ( SqlCommand     cmd  = conn.CreateCommand() )
    using ( SqlDataAdapter sda  = new SqlDataAdapter( cmd ) )
    {
      cmd.CommandText = STORED_PROCEDURE_NAME ;
      cmd.CommandType = CommandType.StoredProcedure ;

      //
      // 1. @iPatientID
      //
      // required
        SqlParameter p1 = new SqlParameter( @"@iPatientID" , SqlDbType.Int ) ;
        if ( @iPatientID == null )
        {
          p1.Value = System.DBNull.Value ;
        }
        else
        {
          p1.Value = @iPatientID ;
        }
        cmd.Parameters.Add( p1 ) ;

      // add return code parameter
      SqlParameter pReturnCode = new SqlParameter() ;
      pReturnCode.SqlDbType = System.Data.SqlDbType.Int ;
      pReturnCode.Direction = System.Data.ParameterDirection.ReturnValue ;
      cmd.Parameters.Add( pReturnCode ) ;

      DataSet ds = new DataSet() ;

      conn.Open() ;
      sda.Fill( ds ) ;
      conn.Close() ;

      DataTable dt    = ( ds.Tables.Count > 0 ? ds.Tables[0] : null ) ;
      this.ResultSet  = ( dt != null && dt.Rows.Count > 0 ? dt.Rows[0] : null ) ;
      this.ReturnCode = (int) pReturnCode.Value ;


    }

    return this.ReturnCode ;

  }

}
现在您只需要处理一个DataRow,如果需要,它的关联DataTable/DataSet等仍然存在。向DataRow类添加扩展方法消除了将每个列向下转换为正确类型的大量繁琐工作。我宁愿写:

int? x = dr.CastAsIntNullable( "myColumn" ) ;
而不是通常的样板

public static class DataRowExtensions
{

    #region downcast to DateTime

    public static DateTime CastAsDateTime( this DataRow row , int index )
    {
        return toDateTime( row[index] ) ;
    }
    public static DateTime CastAsDateTime( this DataRow row , string columnName )
    {
        return toDateTime( row[columnName] ) ;
    }

    public static DateTime? CastAsDateTimeNullable( this DataRow row , int index )
    {
        return toDateTimeNullable( row[index] );
    }
    public static DateTime? CastAsDateTimeNullable( this DataRow row , string columnName )
    {
        return toDateTimeNullable( row[columnName] ) ;
    }

    #region conversion helpers

    private static DateTime toDateTime( object o )
    {
        DateTime value = (DateTime)o;
        return value;
    }

    private static DateTime? toDateTimeNullable( object o )
    {
        bool  hasValue = !( o is DBNull );
        DateTime? value    = ( hasValue ? (DateTime?) o : (DateTime?) null ) ;
        return value;
    }

    #endregion

    #endregion downcast to DateTime

    #region downcast to byte[]

    public static byte[] CastAsByteArray( this DataRow row , int index )
    {
        return toByteArray( row[index] );
    }
    public static byte[] CastAsByteArray( this DataRow row , string columnName )
    {
        return toByteArray( row[columnName] );
    }

    #region conversion helpers

    private static byte[] toByteArray( object o )
    {
        bool   hasValue = !( o is DBNull );
        byte[] value    = ( hasValue ? (byte[]) o : (byte[]) null ) ;
        return value;
    }

    #endregion

    #endregion downcast to Byte[]

    #region downcast to int

    public static int CastAsInt( this DataRow row , int index )
    {
        return toInt( row[index] ) ;
    }
    public static int CastAsInt( this DataRow row , string columnName )
    {
        return toInt( row[columnName] ) ;
    }

    public static int? CastAsIntNullable( this DataRow row , int index )
    {
        return toIntNullable( row[index] );
    }
    public static int? CastAsIntNullable( this DataRow row , string columnName )
    {
        return toIntNullable( row[columnName] ) ;
    }

    #region conversion helpers

    private static int toInt( object o )
    {
        int value = (int)o;
        return value;
    }

    private static int? toIntNullable( object o )
    {
        bool hasValue = !( o is DBNull );
        int? value    = ( hasValue ? (int?) o : (int?) null ) ;
        return value;
    }

    #endregion

    #endregion downcast to int

    #region downcast to decimal

    public static decimal CastAsDecimal( this DataRow row , int index )
    {
        return toDecimal( row[index] ) ;
    }
    public static decimal CastAsDecimal( this DataRow row , string columnName )
    {
        return toDecimal( row[columnName] ) ;
    }

    public static decimal? CastAsDecimalNullable( this DataRow row , int index )
    {
        return toDecimalNullable( row[index] );
    }
    public static decimal? CastAsDecimalNullable( this DataRow row , string columnName )
    {
        return toDecimalNullable( row[columnName] ) ;
    }

    #region conversion helpers

    private static decimal toDecimal( object o )
    {
        decimal value = (decimal)o;
        return value;
    }

    private static decimal? toDecimalNullable( object o )
    {
        bool     hasValue = !( o is DBNull );
        decimal? value    = ( hasValue ? (decimal?) o : (decimal?) null ) ;
        return value;
    }

    #endregion

    #endregion downcast to decimal


    #region downcast to double

    public static double CastAsDouble( this DataRow row , int index )
    {
        return toDouble( row[index] ) ;
    }
    public static double CastAsDouble( this DataRow row , string columnName )
    {
        return toDouble( row[columnName] ) ;
    }

    public static double? CastAsDoubleNullable( this DataRow row , int index )
    {
        return toDoubleNullable( row[index] );
    }
    public static double? CastAsDoubleNullable( this DataRow row , string columnName )
    {
        return toDoubleNullable( row[columnName] ) ;
    }

    #region conversion helpers

    private static double toDouble( object o )
    {
        double value = (double)o;
        return value;
    }

    private static double? toDoubleNullable( object o )
    {
        bool     hasValue = !( o is DBNull );
        double? value    = ( hasValue ? (double?) o : (double?) null ) ;
        return value;
    }

    #endregion

    #endregion downcast to double

    #region downcast to bool

    public static bool CastAsBool( this DataRow row , int index )
    {
        return toBool( row[index] ) ;
    }
    public static bool CastAsBool( this DataRow row , string columnName )
    {
        return toBool( row[columnName] ) ;
    }

    public static bool? CastAsBoolNullable( this DataRow row , int index )
    {
        return toBoolNullable( row[index] );
    }
    public static bool? CastAsBoolNullable( this DataRow row , string columnName )
    {
        return toBoolNullable( row[columnName] ) ;
    }

    #region conversion helpers

    private static bool toBool( object o )
    {
        bool value = (bool)o;
        return value;
    }

    private static bool? toBoolNullable( object o )
    {
        bool  hasValue = !( o is DBNull );
        bool? value    = ( hasValue ? (bool?) o : (bool?) null ) ;
        return value;
    }

    #endregion

    #endregion downcast to bool

    #region downcast to string

    public static string CastAsString( this DataRow row , int index )
    {
        return toString( row[index] );
    }
    public static string CastAsString( this DataRow row , string columnName )
    {
        return toString( row[columnName] );
    }

    #region conversion helpers

    private static string toString( object o )
    {
        bool   hasValue = !( o is DBNull );
        string value    = ( hasValue ? (string) o : (string) null ) ;
        return value;
    }

    #endregion

    #endregion downcast to string

}

我编写了一个代码生成器,生成数据访问代码,每个存储过程一个类。其中一个选项是,您可以指定其结果集由一行组成。用法很简单:生成的代码通过包装器方法调用,包装器方法强制执行用法契约所需的任何约束,因此:

    public DataRow GetPatientData( int patientID )
    {
        dbo_GetPatientData67 sp = new dbo_GetPatientData( CONNECT_STRING_ID ) ;
        int                  rc = sp.Exec( patientID ) ;
        DataRow              dr = sp.ResultSet ;

        if ( dr == null ) throw new InvalidOperationException("nothing returned from stored procedure.") ;
        return dr ;
    }
以下是数据访问代码的要点:

/* generated code. do not change -- generated code */
public class dbo_GetPatientData
{

  public int     ReturnCode { get ; private set ; }
  public DataRow ResultSet { get ; private set ; }

  public int Exec(  int? @iPatientID )
  {
    using ( SqlConnection  conn = new SqlConnection( this._connectString ) )
    using ( SqlCommand     cmd  = conn.CreateCommand() )
    using ( SqlDataAdapter sda  = new SqlDataAdapter( cmd ) )
    {
      cmd.CommandText = STORED_PROCEDURE_NAME ;
      cmd.CommandType = CommandType.StoredProcedure ;

      //
      // 1. @iPatientID
      //
      // required
        SqlParameter p1 = new SqlParameter( @"@iPatientID" , SqlDbType.Int ) ;
        if ( @iPatientID == null )
        {
          p1.Value = System.DBNull.Value ;
        }
        else
        {
          p1.Value = @iPatientID ;
        }
        cmd.Parameters.Add( p1 ) ;

      // add return code parameter
      SqlParameter pReturnCode = new SqlParameter() ;
      pReturnCode.SqlDbType = System.Data.SqlDbType.Int ;
      pReturnCode.Direction = System.Data.ParameterDirection.ReturnValue ;
      cmd.Parameters.Add( pReturnCode ) ;

      DataSet ds = new DataSet() ;

      conn.Open() ;
      sda.Fill( ds ) ;
      conn.Close() ;

      DataTable dt    = ( ds.Tables.Count > 0 ? ds.Tables[0] : null ) ;
      this.ResultSet  = ( dt != null && dt.Rows.Count > 0 ? dt.Rows[0] : null ) ;
      this.ReturnCode = (int) pReturnCode.Value ;


    }

    return this.ReturnCode ;

  }

}
现在您只需要处理一个DataRow,如果需要,它的关联DataTable/DataSet等仍然存在。向DataRow类添加扩展方法消除了将每个列向下转换为正确类型的大量繁琐工作。我宁愿写:

int? x = dr.CastAsIntNullable( "myColumn" ) ;
而不是通常的样板

public static class DataRowExtensions
{

    #region downcast to DateTime

    public static DateTime CastAsDateTime( this DataRow row , int index )
    {
        return toDateTime( row[index] ) ;
    }
    public static DateTime CastAsDateTime( this DataRow row , string columnName )
    {
        return toDateTime( row[columnName] ) ;
    }

    public static DateTime? CastAsDateTimeNullable( this DataRow row , int index )
    {
        return toDateTimeNullable( row[index] );
    }
    public static DateTime? CastAsDateTimeNullable( this DataRow row , string columnName )
    {
        return toDateTimeNullable( row[columnName] ) ;
    }

    #region conversion helpers

    private static DateTime toDateTime( object o )
    {
        DateTime value = (DateTime)o;
        return value;
    }

    private static DateTime? toDateTimeNullable( object o )
    {
        bool  hasValue = !( o is DBNull );
        DateTime? value    = ( hasValue ? (DateTime?) o : (DateTime?) null ) ;
        return value;
    }

    #endregion

    #endregion downcast to DateTime

    #region downcast to byte[]

    public static byte[] CastAsByteArray( this DataRow row , int index )
    {
        return toByteArray( row[index] );
    }
    public static byte[] CastAsByteArray( this DataRow row , string columnName )
    {
        return toByteArray( row[columnName] );
    }

    #region conversion helpers

    private static byte[] toByteArray( object o )
    {
        bool   hasValue = !( o is DBNull );
        byte[] value    = ( hasValue ? (byte[]) o : (byte[]) null ) ;
        return value;
    }

    #endregion

    #endregion downcast to Byte[]

    #region downcast to int

    public static int CastAsInt( this DataRow row , int index )
    {
        return toInt( row[index] ) ;
    }
    public static int CastAsInt( this DataRow row , string columnName )
    {
        return toInt( row[columnName] ) ;
    }

    public static int? CastAsIntNullable( this DataRow row , int index )
    {
        return toIntNullable( row[index] );
    }
    public static int? CastAsIntNullable( this DataRow row , string columnName )
    {
        return toIntNullable( row[columnName] ) ;
    }

    #region conversion helpers

    private static int toInt( object o )
    {
        int value = (int)o;
        return value;
    }

    private static int? toIntNullable( object o )
    {
        bool hasValue = !( o is DBNull );
        int? value    = ( hasValue ? (int?) o : (int?) null ) ;
        return value;
    }

    #endregion

    #endregion downcast to int

    #region downcast to decimal

    public static decimal CastAsDecimal( this DataRow row , int index )
    {
        return toDecimal( row[index] ) ;
    }
    public static decimal CastAsDecimal( this DataRow row , string columnName )
    {
        return toDecimal( row[columnName] ) ;
    }

    public static decimal? CastAsDecimalNullable( this DataRow row , int index )
    {
        return toDecimalNullable( row[index] );
    }
    public static decimal? CastAsDecimalNullable( this DataRow row , string columnName )
    {
        return toDecimalNullable( row[columnName] ) ;
    }

    #region conversion helpers

    private static decimal toDecimal( object o )
    {
        decimal value = (decimal)o;
        return value;
    }

    private static decimal? toDecimalNullable( object o )
    {
        bool     hasValue = !( o is DBNull );
        decimal? value    = ( hasValue ? (decimal?) o : (decimal?) null ) ;
        return value;
    }

    #endregion

    #endregion downcast to decimal


    #region downcast to double

    public static double CastAsDouble( this DataRow row , int index )
    {
        return toDouble( row[index] ) ;
    }
    public static double CastAsDouble( this DataRow row , string columnName )
    {
        return toDouble( row[columnName] ) ;
    }

    public static double? CastAsDoubleNullable( this DataRow row , int index )
    {
        return toDoubleNullable( row[index] );
    }
    public static double? CastAsDoubleNullable( this DataRow row , string columnName )
    {
        return toDoubleNullable( row[columnName] ) ;
    }

    #region conversion helpers

    private static double toDouble( object o )
    {
        double value = (double)o;
        return value;
    }

    private static double? toDoubleNullable( object o )
    {
        bool     hasValue = !( o is DBNull );
        double? value    = ( hasValue ? (double?) o : (double?) null ) ;
        return value;
    }

    #endregion

    #endregion downcast to double

    #region downcast to bool

    public static bool CastAsBool( this DataRow row , int index )
    {
        return toBool( row[index] ) ;
    }
    public static bool CastAsBool( this DataRow row , string columnName )
    {
        return toBool( row[columnName] ) ;
    }

    public static bool? CastAsBoolNullable( this DataRow row , int index )
    {
        return toBoolNullable( row[index] );
    }
    public static bool? CastAsBoolNullable( this DataRow row , string columnName )
    {
        return toBoolNullable( row[columnName] ) ;
    }

    #region conversion helpers

    private static bool toBool( object o )
    {
        bool value = (bool)o;
        return value;
    }

    private static bool? toBoolNullable( object o )
    {
        bool  hasValue = !( o is DBNull );
        bool? value    = ( hasValue ? (bool?) o : (bool?) null ) ;
        return value;
    }

    #endregion

    #endregion downcast to bool

    #region downcast to string

    public static string CastAsString( this DataRow row , int index )
    {
        return toString( row[index] );
    }
    public static string CastAsString( this DataRow row , string columnName )
    {
        return toString( row[columnName] );
    }

    #region conversion helpers

    private static string toString( object o )
    {
        bool   hasValue = !( o is DBNull );
        string value    = ( hasValue ? (string) o : (string) null ) ;
        return value;
    }

    #endregion

    #endregion downcast to string

}

如果是空的呢?DataRow dr=新的DataRow。如果我返回该值,然后尝试访问密钥,它会引发异常吗?如果为空,则返回null并检查null。无论您做什么,如果找不到该行,您都必须进行某种检查并处理该案例。如果该行为空怎么办?DataRow dr=新的DataRow。如果我返回该值,然后尝试访问密钥,它会引发异常吗?如果为空,则返回null并检查null。无论您做什么,如果找不到行,您都必须进行某种检查并处理该情况。如果我的应用程序有几十个存储过程,该怎么办。您会为每个过程创建一个类吗?当然,每个程序都可能返回几十个columns@newkid91当然,我会为每个实体创建一个类。我不想成为坏消息的传递者,但这是这里的最佳实践;DataTable是一个黑客。对于一次性查询,有一个使用dynamic的dapper中的非类型查询。@Marc Gravell您能进一步解释代码的第一部分吗?@Marc Gravell这不是个坏消息。我不是在找一条懒散的出路。我的例子是懒惰的方式。我在寻找正确的方法。@newKid91考虑一下:定义一个类比所有的[客户名称]要健壮得多。我认为您当前的方法从根本上讲是有风险的。如果我的应用程序有几十个存储过程呢。您会为每个过程创建一个类吗?当然,每个程序都可能返回几十个columns@newkid91当然,我会为每个实体创建一个类。我不想成为坏消息的传递者,但这是这里的最佳实践;DataTable是一个黑客。对于一次性查询,有一个使用dynamic的dapper中的非类型查询。@Marc Gravell您能进一步解释代码的第一部分吗?@Marc Gravell这不是个坏消息。我不是在找一条懒散的出路。我的例子是懒惰的方式。我在寻找正确的方法。@newKid91考虑一下:定义一个类比所有的[客户名称]要健壮得多。伊苏 bmit认为您当前的方法从根本上讲是有风险的。