SqlCommand.ExecuteReader 是否会自动打开数据库连接?

发布于 2024-12-02 16:14:32 字数 3354 浏览 1 评论 0原文

我注意到我的网页上有一些奇怪的行为。我有一个 WCF 数据服务,它提供 JSON 来填充 jqGrid。使用 javascript/ajax 调用该服务。

然后我有一些服务器端代码也调用相同的 WCF 服务来获取数据。

在我的 WCF 服务中,我在没有事先打开连接的情况下运行 cmd.ExecuteReader(),并且在某些情况下它不会抱怨连接 - 似乎是当我从 javascript 调用数据服务时。但是,当我从代码隐藏(即服务器端)调用该服务时,我收到错误消息“ExecuteReader 需要一个打开且可用的连接”。

有人知道这个问题吗?我已经尽可能缩小范围了。看来唯一的区别是我是从客户端还是从服务器端调用服务。这是我的代码:

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class JsonService
{
    static String _connection = ConfigMgr.ConnectionStrings["MyConnStr"];
    static DomainEntities dbContext = new DomainEntities(_connection);

    [OperationContract]
    [WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest,
                         RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
    public JsonGrid Get()
    {
        return new JsonGridContract(dbContext.Products.ToJson()); 
    }
}

下面是调用 ExecuteReader() 的地方。它拦截提供程序,将表达式树转换为 SQL,然后执行它,将结果解析为基于字符串的 JSON,而不是域对象。

    public static List<JsonRow> ToJson(this IQueryable queryable)
    {
        Expression expression = queryable.Expression;
        expression = Evaluator.PartialEval(expression);

        if !(queryable.Provider is JsonQueryProvider)
              throw new InvalidOperationException("Provider is invalid");

        String table = (queryable.Provider as JsonQueryProvider).Table; 

        SqlConnection connection = (queryable.Provider as JsonQueryProvider).Connection; 

        Type elementType = TypeSystem.GetElementType(expression.Type);

        TranslateResult result = new QueryTranslator(table).Translate(expression);

        SqlCommand cmd = connection.CreateCommand();

        cmd.CommandText = result.CommandText;

        SqlDataReader reader = cmd.ExecuteReader();

        List<JsonRow> jsonResult = new List<JsonRow>(); 

        while (reader.Read())
            {
                JsonRow instance = new JsonRow(reader.FieldCount); 

                for (int i = 0, n = reader.FieldCount; i < n; i++)
                {
                    var items = instance.Items; 

                    if (reader.IsDBNull(i))
                    {
                        items[i] = string.Empty;  
                    }
                    else
                    {
                        items[i] = reader[i].ToString(); 
                    }
                }

                jsonResult.Add(instance); 
            }

        reader.Close(); 

        return jsonResult; 
    }

如前所述,即使我从未打开连接,此方法也可以正常执行。我正在使用 AJAX 客户端,

  $.ajax(
            {
                type: "POST",
                contentType: "application/json; charset=utf-8",
                url: "Services/JsonService.svc/Get",
                data: {},
                dataType: "json",
                success: function (data, textStatus) {
                    if (textStatus == "success") {
                        var thegrid = $("#jqGrid")[0];
                        thegrid.addJSONData(data.d);
                    }
                },
                error: function (data, textStatus) {
                    alert('An error has occured retrieving data.');
                }
            });
        }

I am noticing some odd behavior on my web page. I have a WCF Data service that serves up JSON to fill up a jqGrid. The service is called using javascript/ajax.

Then I have some server-side code that also calls the same WCF service to get data.

Inside my WCF Service I run cmd.ExecuteReader() without a prior open connection, and it doesn't complain about the connection in certain cases -- it seems to be when I'm calling the data service from javascript. However when I call the service from code-behind, ie server-side, I get the error that says "ExecuteReader requires an open and available connection".

Does anyone know about this issue? Ive narrowed it down as much as possible. It appears the only difference is whether I call the service from client side or server side. Here is my code:

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class JsonService
{
    static String _connection = ConfigMgr.ConnectionStrings["MyConnStr"];
    static DomainEntities dbContext = new DomainEntities(_connection);

    [OperationContract]
    [WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest,
                         RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
    public JsonGrid Get()
    {
        return new JsonGridContract(dbContext.Products.ToJson()); 
    }
}

Below is where ExecuteReader() is called. It intercepts the provider, converts the expression tree to SQL, and then executes it parsing the results to string-based JSON rather than the domain objects.

    public static List<JsonRow> ToJson(this IQueryable queryable)
    {
        Expression expression = queryable.Expression;
        expression = Evaluator.PartialEval(expression);

        if !(queryable.Provider is JsonQueryProvider)
              throw new InvalidOperationException("Provider is invalid");

        String table = (queryable.Provider as JsonQueryProvider).Table; 

        SqlConnection connection = (queryable.Provider as JsonQueryProvider).Connection; 

        Type elementType = TypeSystem.GetElementType(expression.Type);

        TranslateResult result = new QueryTranslator(table).Translate(expression);

        SqlCommand cmd = connection.CreateCommand();

        cmd.CommandText = result.CommandText;

        SqlDataReader reader = cmd.ExecuteReader();

        List<JsonRow> jsonResult = new List<JsonRow>(); 

        while (reader.Read())
            {
                JsonRow instance = new JsonRow(reader.FieldCount); 

                for (int i = 0, n = reader.FieldCount; i < n; i++)
                {
                    var items = instance.Items; 

                    if (reader.IsDBNull(i))
                    {
                        items[i] = string.Empty;  
                    }
                    else
                    {
                        items[i] = reader[i].ToString(); 
                    }
                }

                jsonResult.Add(instance); 
            }

        reader.Close(); 

        return jsonResult; 
    }

As noted before this method executes fine even though I never open the connection. I am using AJAX client-side,

  $.ajax(
            {
                type: "POST",
                contentType: "application/json; charset=utf-8",
                url: "Services/JsonService.svc/Get",
                data: {},
                dataType: "json",
                success: function (data, textStatus) {
                    if (textStatus == "success") {
                        var thegrid = $("#jqGrid")[0];
                        thegrid.addJSONData(data.d);
                    }
                },
                error: function (data, textStatus) {
                    alert('An error has occured retrieving data.');
                }
            });
        }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

墨落画卷 2024-12-09 16:14:32

首先要排除的是,您不要处置任何物品。
您的 SqlCommand、SqlConnection 和 SqlDataReader 都应该使用 stmt 块。
那么你不需要明确的关闭。看看这是否会全面失败。我很好奇是否只是垃圾收集器尚未收集来杀死您的连接对象。

using(SqlConnection connection = new SqlConnection(...))
{
...
}

First thing to exclude, is that you don't dispose any of your objects.
Your SqlCommand, SqlConnection, and SqlDataReader should all be in using stmt blocks.
You don't need your explicit close then. See if this then fails across the board. Im curious if its just that the garbage collector hasn't collected yet to kill your connection object.

using(SqlConnection connection = new SqlConnection(...))
{
...
}
我一向站在原地 2024-12-09 16:14:32

问题是您的 SqlReader 在使用时会保持连接被占用。您不能将该连接用于其他任何用途(例如执行另一个读取器,甚至另一个语句)。在阅读器关闭之前,您的连接本质上是锁定的。

通常,就像 Adam 所说,将您的语句包装在 using 块中,您的问题就会得到解决。但是,我注意到您似乎正在从某种缓存(queryable.Provider)中获取连接。如果您尝试重复使用连接或缓存连接,您将遇到相同的问题。

如果您想利用 ADO.NET 的连接池功能,则需要在 using 块内创建一个新的 Connection 实例,并且仅重用完全相同的连接字符串。 ADO.NET 将自动池化底层连接资源。

The issue is that your SqlReader will keep the Connection occupied while it is in use. You can't use the connection for anything else (such as executing another reader, or even another statement). Until the reader is closed, your connection is essentially locked.

Typically, like Adam says, wrap your statements in a using block, and your issue will be solved. However, I notice you seem to be grabbing your connection from a cache of some sort (queryable.Provider). If you are attempting to use your connection repetitively, or caching it, you will run into the same issue.

If you want to take advantage of ADO.NET's connection pooling abilities, you need to create a new Connection instance inside your using block, and only reuse the EXACT same connection string. ADO.NET will pool the underlying connection resources automatically.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文