gpt4 book ai didi

c# - 这个共享的 DbCommand 对象线程安全吗?

转载 作者:太空宇宙 更新时间:2023-11-03 14:20:19 25 4
gpt4 key购买 nike

我不明白为什么每次需要调用存储过程时都必须创建一个 DbCommand 对象。所以我试图想出一种方法来做到这一点。我已经测试了我的代码(见下文)。但我想与社区核实一下,以防我遗漏了什么。我会在 ASP.NET 应用程序中使用它。这段代码线程安全吗?

SharedDbCommand - 包装了 DbCommand 对象的创建和存储

Db - 数据库的包装器,通过静态字段和 ThreadStatic 属性使用 SharedDbCommand

程序 - 启动线程并使用 Db 对象的控制台应用程序

// SharedDbCommand.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Data.Common;
using System.Data.SqlClient;
using System.Data;

namespace TestCmdPrepare {
public class SharedDbCommand {
[ThreadStatic]
static DbCommand cmd;

public SharedDbCommand(string procedureName, ConnectionStringSettings dbConfig) {
var factory = DbProviderFactories.GetFactory(dbConfig.ProviderName);
cmd = factory.CreateCommand();
cmd.Connection = factory.CreateConnection();
cmd.Connection.ConnectionString = dbConfig.ConnectionString;
cmd.CommandText = procedureName;
cmd.CommandType = System.Data.CommandType.StoredProcedure;
if (cmd is SqlCommand) {
try {
cmd.Connection.Open();
SqlCommandBuilder.DeriveParameters(cmd as SqlCommand);
} finally {
if (cmd != null && cmd.Connection != null)
cmd.Connection.Close();
}
}
}

public DbParameter this[string name] {
get {
return cmd.Parameters[name];
}
}

public IDataReader ExecuteReader() {
try {
cmd.Connection.Open();
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
} finally {
cmd.Connection.Close();
}
}

public void ExecuteNonQuery() {
try {
cmd.Connection.Open();
cmd.ExecuteNonQuery();
} finally {
cmd.Connection.Close();
}
}
}
}

// Db.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Data.Common;
using System.Data;
using System.Data.SqlClient;
using System.Threading;
using System.Diagnostics;

namespace TestCmdPrepare {
public class Db {
ConnectionStringSettings dbSettings;
DbProviderFactory factory;
public Db() {
dbSettings = ConfigurationManager.ConnectionStrings["db"];
factory = DbProviderFactories.GetFactory(dbSettings.ProviderName);
}
IDataReader ExecuteReader(DbCommand cmd) {
cmd.Connection.Open();
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}

private DbConnection CreateConnection() {
var c = factory.CreateConnection();
c.ConnectionString = dbSettings.ConnectionString;
return c;
}

DbCommand CreateCommand(string procedureName) {
var cmd = factory.CreateCommand();
cmd.Connection = CreateConnection();
cmd.CommandText = "get_stuff";
cmd.CommandType = CommandType.StoredProcedure;
if (cmd is SqlCommand) {
try {
cmd.Connection.Open();
SqlCommandBuilder.DeriveParameters(cmd as SqlCommand);
} finally {
cmd.Connection.Close();
}
}
return cmd;
}


[ThreadStatic]
static DbCommand get_stuff;

DbCommand GetStuffCmd {
get {
if (get_stuff == null)
get_stuff = CreateCommand("get_stuff");
return get_stuff;
}
}

public string GetStuff(int id) {
GetStuffCmd.Parameters["@id"].Value = id;
using (var reader = ExecuteReader(GetStuffCmd)) {
if (reader.Read()) {
return reader.GetString(reader.GetOrdinal("bar"));
}
}
return null;
}

[ThreadStatic]
static SharedDbCommand get_stuff2;
public string GetStuff2(int id) {
if (get_stuff2 == null)
get_stuff2 = new SharedDbCommand("get_stuff", dbSettings);
get_stuff2["@id"].Value = id;
using (var reader = get_stuff2.ExecuteReader()) {
if (reader.Read()) {
Thread.Sleep(1000);
return reader.GetString(reader.GetOrdinal("bar"));
}
}
return null;
}
}
}


// Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
using System.Configuration;
using System.Data.SqlClient;
using System.Threading;

namespace TestCmdPrepare {
class Program {
static void Main(string[] args) {
var db = new Db();
var threads = new List<Thread>();
for (int i = 0; i < 4; i++) {
var one = new Thread(Run2);
var two = new Thread(Run1);

threads.Add(one);
threads.Add(two);
one.Start();
two.Start();

Write(db, 1);
Write(db, 2);
}
Console.WriteLine("Joining");
foreach (var thread in threads) {
thread.Join();
}
Console.WriteLine();
Console.WriteLine("DONE");
Console.ReadLine();
return;
}

static void Write(Db db, int id) {


Console.WriteLine("2:{0}:{1}", Thread.CurrentThread.ManagedThreadId, db.GetStuff2(id));
Console.WriteLine("1:{0}:{1}", Thread.CurrentThread.ManagedThreadId, db.GetStuff(id));
}

static void Run1() {
var db = new Db();
Write(db, 1);
}

static void Run2() {
var db = new Db();
Write(db, 2);
}

}
}

最佳答案

出于很多原因,这是个坏主意。其他人已经提到了其中的一些,但我会给你一个特定于你的实现的:在 ASP.NET 中使用 ThreadStatic 最终让你失望(参见 here )。您无法控制管道,因此可能多个线程最终为一个请求提供服务(考虑页面上的事件处理程序等——有多少代码不是您的?) .由于未处理的异常,在您不拥有的请求线程上孤立数据也很容易。可能不是一个引人注目的问题,但最好的情况是您正在查看内存泄漏和增加的服务器资源使用,而您的连接就在那里......

我建议您只让 ConnectionPool 完成它的工作 - 它有一些缺点,但与 ASP.NET 管道中发生的其他事情相比,性能不是其中之一。如果您真的想这样做,至少考虑将连接对象存储在 HttpContext.Current.Items 中。您可能可以在有限的情况下完成这项工作,但总会有陷阱,尤其是当您开始编写并行代码时。

去过那里的人只需 0.02 美元。 :)

关于c# - 这个共享的 DbCommand 对象线程安全吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5616083/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com