在穷人的ORM中打开多个SQLConnections的性能影响

时间:2012-05-30 22:22:02

标签: asp.net sql-server vb.net performance

我正在实现一个穷人的ORM和一个遗留应用程序(.NET 2.0 Web应用程序,手工编写的SQL查询)。我有两个数据类:CustomerOrder

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拉出,我必须再次访问数据库(打开另一个连接),并获取该给定客户的订单。如果我没有误会,就不会需要很多记录来积累大量的开放连接。

我的问题是双重的:

  1. 如何确定正在建立的新连接数以及是否会遇到任何连接池或其他此类.NET限制?
  2. 有更好的方法吗? (见下面的评论)

  3. 我曾经想到的一个想法是将在调用类中创建的SQLConnection对象传递给getOrdersByCustomerId函数,并让该函数使用(显然是?)已经打开的连接。我没有测试它,因为我不知道如何确定它是否比我现有的方法更好。想法?


    背景:

    我正在创建一个搜索Web服务,它返回JSON以供客户端处理。该服务采用单个搜索参数,对不同的表执行多次查找,然后返回自定义JSON对象。例如,如果用户输入看似名称的内容,我会在Customers表中搜索顶部 n 客户列表,并在Orders表中搜索顶部 n 订单,其客户具有该名称。

4 个答案:

答案 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对象在使用后通过使用语句以上述类似的方式处理。所以没有必要进行迭代来添加记录。

再次访问数据库

您是否可以考虑在此处使用ViewStateSession,如果您发现数据有任何变化,您也可以使用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。