我正在实现一个穷人的ORM和一个遗留应用程序(.NET 2.0 Web应用程序,手工编写的SQL查询)。我有两个数据类:Customer
和Order
:
Public Class Customer
Public Property CustomerId As Integer
Public Property CustomerName As String
Public Property Orders As List(Of Order)
End Class
Public Class Order
Public Property OrderId As Integer
Public Property OrderItem As String
End Class
我正在使用SqlDataReader
手动将SQL结果映射到Customer
类的实例:
Dim connection As New SqlConnection(connectionString)
Dim command As New SqlCommand(sql, connection)
Dim reader As SqlDataReader()
connection.Open()
reader = command.ExecuteReader()
Dim customer As New Customer()
While reader.Read
With customer
.CustomerId = reader("CustomerId")
.CustomerName = reader("CustomerName")
.Orders = getOrdersByCustomerId(reader("CustomerId")) ' get orders
End With
End While
connection.Close()
要填充Customer.Orders
,我调用一个返回List(Of Order)
的函数:
Private Function getOrdersByCustomerId(ByVal customerId As Integer) As List(Of Order)
Dim connection As New SqlConnection(connectionString)
Dim command As New SqlCommand(sql, connection)
Dim reader As SqlDataReader()
connection.Open()
reader = command.ExecuteReader()
Dim orders As New List(Of Order)
While reader.Read
Dim order As New Order
With order
.OrderId = reader("OrderId")
.OrderItem = reader("OrderItem")
End With
orders.Add(order)
End While
connection.Close()
Return orders
End Function
我担心这种方法的表现,即如果我拉多个Customer
的话。对于每个Customer
拉出,我必须再次访问数据库(打开另一个连接),并获取该给定客户的订单。如果我没有误会,就不会需要很多记录来积累大量的开放连接。
我的问题是双重的:
我曾经想到的一个想法是将在调用类中创建的SQLConnection
对象传递给getOrdersByCustomerId
函数,并让该函数使用(显然是?)已经打开的连接。我没有测试它,因为我不知道如何确定它是否比我现有的方法更好。想法?
背景:
我正在创建一个搜索Web服务,它返回JSON以供客户端处理。该服务采用单个搜索参数,对不同的表执行多次查找,然后返回自定义JSON对象。例如,如果用户输入看似名称的内容,我会在Customers
表中搜索顶部 n 客户列表,并在Orders
表中搜索顶部 n 订单,其客户具有该名称。
答案 0 :(得分:3)
打开并重新打开连接几乎是免费的。因此,最好打开一个连接并尽快处理它,最好是在一个使用块中。
您可以通过将Max Pool Size=1;
添加到连接字符串来测试连接泄漏。这会将可用连接数限制为1.如果泄漏连接,将会出错。请勿在生产中使用此设置。
为了提高性能,您通常会避免在大型集合中的每一行都访问数据库。因此,您可以编写一个新的查询或过程,返回所有相关客户的所有订单。但是在实践中这是一个不寻常的查询。您通常会显示一个客户列表,并且只有当最终用户放大该客户时才会检索订单列表。
答案 1 :(得分:3)
首先,您错过了Using Statement
,因此您的代码应该是这样的,而不是依靠Garbage Collector
自动处理它。
Using connection As New SqlConnection("connectionString")
Using command As New SqlCommand("sql", connection)
Using reader As SqlDataReader = Nothing
connection.Open()
reader = command.ExecuteReader()
Dim customer As New Customer()
While reader.Read
If True Then
customer.CustomerId = reader("CustomerId")
customer.CustomerName = reader("CustomerName")
customer.Orders = getOrdersByCustomerId(reader("CustomerId"))
End If
End While
connection.Close()
End Using
End Using
End Using
返回有关效果的原始查询
如果记录数量很大,我会使用DataTable
。请记住,DataTable
对象在使用后通过使用语句以上述类似的方式处理。所以没有必要进行迭代来添加记录。
再次访问数据库
您是否可以考虑在此处使用ViewState
或Session
,如果您发现数据有任何变化,您也可以使用SQLDependency
。
<强> getOrdersByCustomerId(读取器(&#34;客户ID&#34))强>
您是否可以将所有与客户相关的信息带到表单加载中,并将其保存在ViewState
中?在数据读取器中不首选反复访问数据库。因此,最后访问DataReader迭代中的ViewState
变量。
答案 2 :(得分:2)
在这个问题的数据库方面工作。
您没有显示用于检索记录的sql命令的任何内容
例如,当您按ID加载客户时,请尝试加载其订单。
SELECT CUSTOMERS.*, ORDERS.*
FROM CUSTOMERS LEFT JOIN ORDERS
ON CUSTOMERS.ID = ORDERS.CUSTOMERID
WHERE CUSTOMERS.ID = @custID
将此数据存储在内存中,无需再往返数据库即可创建客户及其订单。
答案 3 :(得分:2)
不要担心连接,除非你已经关闭了,如果你有,你就不会问这个问题。
如果你想让事情更清楚,虽然使用块会是一个好主意。
Using connection = new SqlConnection(ConnectionString)
' Insert code here
End Using
连接将在最后使用,因为您处于默认行为,连接将返回到池中,以便在下次代码(实际池由appdomain完成)需要时重用。如果在默认时间段内没有任何需要(我似乎记得2分钟),那么它就完全处理了。
PS使用块来实现任何实现方法本地IDisposable的东西是个好主意。
为每个订单点击服务器,没有明确的答案。获得一个订单页面,然后从中创建订单对象集合可以提高性能,只要您没有得到一些您不太可能需要的东西。 即只获取显示所需的数据,如果有负载更多使用某种延迟加载方案并在需要时获取数据,或者具有不同的类,例如OrderSummary,OrderDetail。