目前我使用DataSet
从ADO.NET SQL提供程序读取数据,但我读到DataReader
对某些情况更有效。很多次我只阅读单个/几个记录,不需要内存数据或多个表关系操作。
是否有更清晰有效的阅读方式?
我能否以简单的方式将完整记录直接映射到Agent类,而不必像现在一样解析每一列?
编辑: - 我对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();
....
}
}
答案 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