- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我不明白为什么每次需要调用存储过程时都必须创建一个 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/
如何将 DbCommand 参数复制到另一个 DbCommand,我想要一个新的 DbCommand,其参数与我上一个 DbCommand 相同。但是现在使用不同的 sql 字符串。 最佳答案 //
我有一个文件集合,对于每个文件,我将在事务中使用 Dbcommand 调用一个 SP。 例如: DbCommand insert = db.GetStoredProcCommand("Insert
您好,我正在尝试为应该在 Visual Studio 2013 中使用的 DbCommand 对象创建自定义可视化工具。 我有以下代码 using VisualizerTest; using Micr
我们使用 Enterprise Library 3.0 访问 Oracle DB(Microsoft Oracle 客户端)。如果在调用存储过程或函数后不释放 DbCommand 实例,会发生什么情况
我正在开发一个使用 ASP.NET Core 2.1 和 EF Core 2.1 的项目。虽然大部分查询和命令使用EF,但有些单元需要直接调用存储过程。 我不能使用FromSql,因为它需要基于实体模
我似乎有两个选择: 让我的类(class)实现IDisposable .创建我的DbCommand实例为 private readonly字段,并在构造函数中添加它们使用的参数。每当我想写入数据库时
我想创建一个新的 DbCommand,但出现了一些错误: DbCommand insertcommand = new DbCommand("insert into persons(firstname,
我想创建一个新的 DbCommand,但出现了一些错误: DbCommand insertcommand = new DbCommand("insert into persons(firstname,
我在一些生成代码分析警告的遗留代码中看到了这一点: Database db = DatabaseFactory.CreateDatabase(strDBCon); DbCommand dbCo
在我试图理解的软件中,DataReader 类是通过 ExecuteReader() 从 IDbCommand 类型(继承的)接收的。 我对 C# 和数据库通信还很陌生,所以:DataReader 如
我有一个参数添加函数,如下所示: public static void AddParameter(DbCommand comm, string ParamName, Object objValue,
我正在尝试拦截所有 SQL 命令并将其记录到数据库中。我还需要参数及其值。但是 command.Parameters 没有 IEnumerable。我可以使用 for-statement,但感觉有点像
我有以下代码尝试从两个不同的表中获取记录,然后将它们添加到特定的组合框中。只有第一个查询有效,第二个查询被忽略。 Try sqlConn = New MySqlConnecti
我不明白为什么每次需要调用存储过程时都必须创建一个 DbCommand 对象。所以我试图想出一种方法来做到这一点。我已经测试了我的代码(见下文)。但我想与社区核实一下,以防我遗漏了什么。我会在 ASP
我需要一个带参数的更新命令,由于某些原因我不能使用存储过程,实际上我们生成更新命令依赖于数据库、表和列,我们使用以下形式: string conStr = "Provider=SQLN
我正在构建一个 sqlwrapper 来处理 MSsql 和 Sqlite,到目前为止,我使用通用 dbconnection、数据集和 dataadapter 没有问题,但是使用 dbcommand
如果我已经使用 ,我有以下 ado.net 代码使用 要包装我的 DBCommand,我是否必须明确关闭下面的连接? 谢谢, public static void ExecuteSQL(
我正在使用 DbCommand 来自:System.ComponentModel.Component 我正在构建一个带有参数的对象: DbCommand command = _webERPDB.Get
您好,我在使用 Devart 时遇到过这种异常情况。我在 MySql 中调用存储过程。存储过程函数,如果我通过数据库调用它。 using (dc = conn.GetContext())
我们有一个超过 13 年历史的 SQL 帮助程序库,所有应用程序都在使用它。我想更新它以使用 Enterprise Library DAAB。这样做的一个障碍是难以将 SqlCommand(由库和所有
我是一名优秀的程序员,十分优秀!