需要更正或更好的方法来通过SQL ADO.NET读取数据

时间:2011-01-07 14:26:56

标签: .net ado.net

目前我使用DataSet从ADO.NET SQL提供程序读取数据,但我读到DataReader对某些情况更有效。很多次我只阅读单个/几个记录,不需要内存数据或多个表关系操作。

  1. 是否有更清晰有效的阅读方式?

  2. 我能否以简单的方式将完整记录直接映射到Agent类,而不必像现在一样解析每一列?

  3. 编辑: - 我对ORM进行了一些调查并稍微使用了Linq2SQL,但是熟悉度曲线看起来不那么陡峭,所以就这样了。请在.NET中提供其他可用方法,而不是任何外部工具。

    public override Agent GetAgentByAgentId(string agentId)
    {
       Agent agent;
    
       try
       {
          ArrayList paramList = new ArrayList();
          paramList.Add(_dbUtilities.CreateSqlParamater("@agent_id", SqlDbType.VarChar, 10, ParameterDirection.Input, agentId));
    
          // Query the database for an agent with given agentId
          DataSet ds = _dbLib.ExecuteProcedureDS("sp_dbc_agentsSelect", paramList);
    
          if (ds != null && ds.Tables != null && ds.Tables.Count > 0)
          {
             DataTable dt = ds.Tables[0];
    
             if (dt.Rows.Count > 0)
             {
                agent = new Agent();
                DataRow dr = dt.Rows[0];
    
                // Get the agent data
                agent.IsActive = bool.Parse(dr["is_active"].ToString());
                agent.UserId = dr["user_id"].ToString();
                ....
              }
            }
    

4 个答案:

答案 0 :(得分:2)

您可以使用ORM,例如Microsoft Entity Framework 它们将自动生成SQL和复制属性到业务对象。

答案 1 :(得分:2)

首先,我想推荐SLak的回答。这真的是你问题的答案。我理解您对使用像EF这样的大型工具集的担忧,但它确实是正确的解决方案,没有您想象的那么多的学习曲线。几乎没有理由直接进入像DataReader这样的事情。 EF是.NET的一部分,我不鼓励你使用它。

同样地,不会继续创建自己的ORM,包括装饰和自动代码生成等。做。不。做。它。最后,你将花费尽可能多的时间来维护你的业务逻辑,并且你会因为没有使用比我更聪明的人的工作而踢你自己,也可能是你;) (当然没有冒犯)。如果你正在寻找快速和肮脏的,那么你需要快速而肮脏。

话虽如此,使用DataReader 从数据库中读取数据的最轻量级方法。

我不确定如何回答您的第一个问题,只是举例说明在C#中使用DataReader

using(DbConnection conn = new SqlConnection("connection string"0)
{
    conn.Open();

    using(DbCommand cmd = conn.CreateCommand())
    {
        cmd.CommandType = CommandType.Text;
        cmd.CommandText = "sql statement";

        using(IDataReader reader = cmd.ExecuteReader())
        {
            while(reader.Read())
            {
                var column1 = reader["column1"];
            }
        }
    }
}

while语句将对从阅读器检索到的每一行执行,但如果需要,您当然可以将该循环短路,而不是解析整个结果集。

答案 2 :(得分:1)

回到原点直接从ORM中学习SQL和ADO激发了我创建一种简单的数据提取方式而不是你做的方式(以及我以前的方式)。

这里有很多代码要展示,但我会简要介绍一下如何处理这个问题。

创建一个继承自Column

Attribute

Agent课程上装饰您的属性,如下所示:

[Column("is_active")]
public bool IsActive { get; set; }

[Column("user_id")]
public int UserId { get; set; }

从这一点开始,您应该能够从类Properties中获取这些自定义属性并获取列并设置属性值:

property.SetValue(t, value, null);

如果你做对了,你应该得到类似的东西:

agent = dt.Rows[0].ExtractAs<Agent>();

该方法的签名可以是:

public static T ExtractAs<T>(this DataRow row) where T : class, new() 
{ 
    /* map columns to properties */ 
}

答案 3 :(得分:0)

我目前使用n层环境进行开发。我有一个核心数据类,我的所有数据层类都继承自单个数据库连接。它有执行我的存储过程或t-sql的方法。这些方法可以返回业务对象用来加载变量的sqlreader对象。我会发一些例子。我希望这会有所帮助。

这是我的核心数据类:

Imports System.Data
Imports System.Data.SqlClient

Namespace Datalayer
''' <summary>
''' Base class to connect to a database using a single connection.
''' </summary>
''' <remarks>
''' jwames - 12/20/2010 - original code
''' </remarks>
Public Class CoreDataClass
    Implements IDisposable

#Region "attributes"
    Private _connection As SqlConnection
    Private _connectionString As String = My.Settings.PowerNetPricingDB()
    Private _disposedValue As Boolean
    Private _pool As Boolean
#End Region

#Region "constructor"
    ''' <summary>
    ''' Initialize the connection string
    ''' </summary>
    ''' <remarks>
    ''' jwames - 12/20/2010 - original code
    ''' </remarks>
    Public Sub New()
        _connection = New SqlConnection(_connectionString)
        _disposedValue = False
        _pool = False
    End Sub
#End Region

#Region "functions"
    ''' <summary>
    ''' IDisposable implementation
    ''' </summary>
    ''' <param name="disposing"></param>
    ''' <remarks>
    ''' jwames - 12/10/2010 - original code
    ''' </remarks>
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not _disposedValue Then
            If disposing Then
                If _connection Is Nothing = False Then _connection.Dispose()
            End If

            _connectionString = Nothing
        End If

        _disposedValue = True
    End Sub

    ''' <summary>
    ''' Dispose of the database connection objects
    ''' </summary>
    ''' <remarks>
    ''' jwames - 12/20/2010 - original code
    ''' </remarks>
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    ''' <summary>
    ''' Base function used to execute queries that return results
    ''' </summary>
    ''' <remarks>
    ''' jwames - 12/20/2010 - generated by original code
    ''' </remarks>
    Protected Function GetDataReader(ByRef cmd As SqlCommand) As SqlDataReader
        Try
            cmd.CommandType = CommandType.StoredProcedure

            If Me.Connection.State = ConnectionState.Closed Then Me.Connection.Open()

            If _pool Then
                Return cmd.ExecuteReader()
            Else
                Return cmd.ExecuteReader(CommandBehavior.CloseConnection)
            End If
        Catch ex As SqlException
            Throw ex
        End Try
    End Function

    ''' <summary>
    ''' Base function used to execute dynamic queries that return results
    ''' </summary>
    ''' <remarks>
    ''' jwames - 12/20/2010 - original code
    ''' </remarks>
    Protected Function GetDataReader(ByVal sql As String) As SqlDataReader
        Try
            Dim cmd As New SqlCommand(sql, Me.Connection)
            cmd.CommandType = CommandType.StoredProcedure

            If Me.Connection.State = ConnectionState.Closed Then Me.Connection.Open()

            If _pool Then
                Return cmd.ExecuteReader()
            Else
                Return cmd.ExecuteReader(CommandBehavior.CloseConnection)
            End If
        Catch ex As SqlException
            Throw ex
        End Try
    End Function

    ''' <summary>
    ''' Base function that will execute a procedure
    ''' </summary>
    ''' <remarks>
    ''' jwames - 12/20/2010 - original code
    ''' </remarks>
    Protected Sub ExecuteCommand(ByVal cmd As SqlCommand)
        Try
            cmd.CommandType = CommandType.StoredProcedure

            If Me.Connection.State = ConnectionState.Closed Then Me.Connection.Open()

            cmd.ExecuteNonQuery()

            If _pool = False Then
                Me.Connection.Close()
                Me.Connection.Dispose()
            End If
        Catch ex As SqlException
            Throw ex
        End Try
    End Sub

    ''' <summary>
    ''' Base function that will execute a procedure
    ''' </summary>
    ''' <remarks>
    ''' jwames - 12/20/2010 - original code
    ''' </remarks>
    Protected Sub ExecuteCommand(ByVal sql As String)
        Try
            Dim cmd As New SqlCommand(sql, Me.Connection)
            cmd.CommandType = CommandType.StoredProcedure

            If Me.Connection.State = ConnectionState.Closed Then Me.Connection.Open()

            cmd.ExecuteNonQuery()

            If _pool = False Then
                Me.Connection.Close()
                Me.Connection.Dispose()
            End If
        Catch ex As SqlException
            Throw ex
        End Try
    End Sub
#End Region

#Region "properties"
    Public Property Connection() As SqlConnection
        Get
            Return _connection
        End Get
        Set(ByVal value As SqlConnection)
            _connection = value
        End Set
    End Property

    ''' <summary>
    ''' Use the same connection to run multiple queries
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks>
    ''' jwames - 12/20/2010 - original code
    ''' </remarks>
    Public Property PoolConnections() As Boolean
        Get
            Return _pool
        End Get
        Set(ByVal value As Boolean)
            _pool = value
        End Set
    End Property
#End Region


End Class
End Namespace

这是一个简单的数据层类:

Imports System.Data.SqlClient

Namespace Datalayer
''' <summary>
''' Class that connects to the database and relays information to the business layer
''' </summary>
''' <remarks>
''' jwames - 1/3/2011 - original code
''' </remarks>
Public Class ItemCost
    Inherits CoreDataClass

#Region "functions"
    ''' <summary>
    ''' Function that returns all rows within the database
    ''' </summary>
    ''' <remarks>
    ''' jwames - 1/4/2011 - original code
    ''' </remarks>
    Public Function GetAllItemCost(ByVal BranchId As String, ByVal ItemNumber As String, ByVal ShipDate As DateTime) As SqlDataReader
        Dim cmd As New SqlCommand("spGetAllItemCostByItem", Connection)
        cmd.Parameters.AddWithValue("@BranchId", BranchId)
        cmd.Parameters.AddWithValue("@ItemNumber", ItemNumber)
        cmd.Parameters.AddWithValue("@ShipDate", ShipDate)

        Return GetDataReader(cmd)
    End Function

    ''' <summary>
    ''' Function that returns a single row from the database
    ''' </summary>
    ''' <remarks>
    ''' jwames - 1/3/2011 - generated by TierCreator
    ''' </remarks>
    Public Function GetItemCost(ByVal BranchId As String, ByVal ItemNumber As String, ByVal CostTypeId As String, ByVal ShipDate As DateTime) As SqlDataReader
        Dim cmd As New SqlCommand("spGetItemCost", MyBase.Connection)
        cmd.Parameters.AddWithValue("@BranchId", BranchId)
        cmd.Parameters.AddWithValue("@ItemNumber", ItemNumber)
        cmd.Parameters.AddWithValue("@CostTypeId", CostTypeId)
        cmd.Parameters.AddWithValue("@ShipDate", ShipDate)

        Return GetDataReader(cmd)
    End Function

    ''' <summary>
    ''' Move all of the records from the staging tables to the working tables
    ''' </summary>
    ''' <remarks>
    ''' jwames - 1/4/2011 - original code
    ''' </remarks>
    Public Sub PublishItemCost()
        ExecuteCommand("spPublishItemCost")
    End Sub


    ''' <summary>
    ''' Create/Update a row in the database
    ''' </summary>
    ''' <remarks>
    ''' jwames - 1/3/2011 - generated by TierCreator
    ''' </remarks>
    Public Sub SaveItemCost(BranchId As String, ItemNumber As String, CostTypeId As String, EffectiveDate As DateTime, Cost As Double)
        Dim cmd As New SqlCommand("spSaveItemCost", MyBase.Connection)
        cmd.Parameters.AddWithValue("@BranchId", BranchId)
        cmd.Parameters.AddWithValue("@ItemNumber", ItemNumber)
        cmd.Parameters.AddWithValue("@CostTypeId", CostTypeId)
        cmd.Parameters.AddWithValue("@EffectiveDate", EffectiveDate)
        cmd.Parameters.AddWithValue("@Cost", Cost)

        ExecuteCommand(cmd)
    End Sub

#End Region

End Class
End Namespace

这是一个使用此数据层类的业务层类。要注意的部分是加载和保存方法。主要是负载。

Imports System.Text

Namespace BusinessLayer
''' <summary>
''' Class representation of a row in the table in the database
''' </summary>
''' <remarks>
''' jwames - 1/3/2011 - original code
''' </remarks>
Public Class ItemCost
#Region "attributes"
    Private _CostType As CostType
    Private _EffectiveDate As DateTime
    Private _Cost As Double
    Private _exits As Boolean
#End Region

#Region "constructor"
    ''' <summary>
    ''' Initialize all of the module level variables
    ''' </summary>
    ''' <remarks>
    ''' jwames - 1/5/2011 - original code
    ''' </remarks>
    Public Sub New()
        _exits = False
    End Sub

    ''' <summary>
    ''' Initialize all of the module level variables and execute the load method
    ''' </summary>
    ''' <remarks>
    ''' jwames - 1/3/2011 - original code
    ''' </remarks>
    Public Sub New(ByVal BranchId As String, ByVal ItemNumber As String, ByVal CostTypeId As String, ByVal ShipDate As DateTime)
        Me.New()
        Load(BranchId, ItemNumber, CostTypeId, ShipDate)
    End Sub
#End Region

#Region "functions"
    ''' <summary>
    ''' Get a single row based on the arguments passed and set the class variables with the results
    ''' </summary>
    ''' <remarks>
    ''' jwames - 1/5/2011 - original code
    ''' </remarks>
    Public Sub Load(ByVal MyBranchId As String, ByVal MyItemNumber As String, ByVal MyCostTypeId As String, ByVal ShipDate As DateTime)
        ' set these vars to perform validation
        Dim branchId As New Branch(MyBranchId)
        Dim itemNum As New ItemNumber(MyItemNumber)
        CostType.SetCostType(MyCostTypeId)

        Using ic As New Datalayer.ItemCost
            Using rdr As Data.SqlClient.SqlDataReader = ic.GetItemCost(branchId.Abbreviation, itemNum.Number, CostType.GetCostTypeAsString(), ShipDate)
                If rdr.Read() Then
                    If rdr.IsDBNull(0) = False Then CostType.SetCostType(rdr.GetString(0))
                    If rdr.IsDBNull(1) = False Then EffectiveDate = rdr.GetDateTime(1)
                    If rdr.IsDBNull(2) = False Then Cost = rdr.GetDecimal(2)

                    _exits = True
                Else
                    _exits = False
                End If

                rdr.Close()
            End Using
        End Using
    End Sub

    ''' <summary>
    ''' Save the class information in the database
    ''' </summary>
    ''' <remarks>
    ''' jwames - 1/5/2011 - original code
    ''' </remarks>
    Public Shared Sub Save(ByVal BranchId As String, ByVal ItemNumber As String, ByVal CostTypeId As String, ByVal EffectiveDate As DateTime, _
                           ByVal Cost As Double)
        ' set these vars to perform validation
        Dim branch As New Branch(BranchId)
        Dim itemNum As New ItemNumber(ItemNumber)
        Dim ct As New CostType(CostTypeId)

        Using ic As New Datalayer.ItemCost
            ic.SaveItemCost(branch.Abbreviation, itemNum.Number, ct.GetCostTypeAsString(), EffectiveDate, Cost)
        End Using
    End Sub
#End Region

#Region "properties"
    Public Property CostType() As CostType
        Get
            Return _CostType
        End Get
        Set(ByVal value As CostType)
            _CostType = value
        End Set
    End Property

    Public Property EffectiveDate As DateTime
        Get
            Return _EffectiveDate
        End Get
        Set(ByVal value As DateTime)
            _EffectiveDate = value
        End Set
    End Property 

    Public Property Cost() As Double
        Get
            Return _Cost
        End Get
        Set(ByVal value As Double)
            _Cost = value
        End Set
    End Property

    ''' <summary>
    ''' signifies if the item cost was found and loaded in the load method
    ''' </summary>
    ''' <returns>true if found</returns>
    ''' <remarks>
    ''' jwames - 1/5/2011 - original code
    ''' </remarks>
    Public ReadOnly Property Exists() As Boolean
        Get
            Return _exits
        End Get
    End Property
#End Region
End Class
End Namespace