whi*_*key 8 c# multithreading database-connection
首先,我是Java程序员,我是C#的新手,我需要C#开发人员的意见.我正在开发一个连接到数据库(firebird 1.5)的应用程序,查询一些数据并返回给我,所以没有什么可复杂的,但不幸的是我陷入了一些困境:
我们知道数据库连接应该在单独的线程中实现,因为它是一个高权重操作,并且所有连接都应该在连接池中,以便重用已经打开的连接而不是创建新连接.
所以这里是我的第一个问题 - 如何正确组织连接池? (连接池怎么样我读过通常连接池已经由数据提供者实现了,我可以在连接参数中设置它,就像"connectionBuilder.Pooling = true;")
查询怎么样?我的意思是我总是使用每个线程的查询(我认为这是正确的,因为我们也做了一个高权重的操作,我错了吗?无论如何,我很高兴看到你组织数据库工作的最佳实践)和Java我只是通过使用这样的接口和匿名类来从单独的线程返回Query结果:
在DBHelper.class中(DBHelper是单例)
public interface QueryListener {
public void onSuccess(ArrayList<?>);
public void onError(Exception e);
}
public synchronized void getPromoActions(final QueryListener listener) {
if (listener != null) {
try {
ArrayList<String> myPromoActions;
.............
// some query's code
.....
listener.onSucces(myPromoActions);
} catch(Exception e) {
listener.onError(e);
} finally {
closeDatabase();
}
}
}
Run Code Online (Sandbox Code Playgroud)
在某些UI类中(对于eX示例MainWindow)
public void getPromoActions(){
new Thread(new Runnable() {
@Override
public void run() {
DBHelper.getInstance().getPromoActions(new QueryListener() {
@Override
public void onSuccess(ArrayList<?>) {
// set Data to UI element such as Table
}
@Override
public void onError(Exception e){
// Handling exception
}
});
}
}).start();
}
Run Code Online (Sandbox Code Playgroud)
在C#中,我应该使用委托来标记哪个方法将在线程中执行,但不幸的是我不能将任何回调作为参数发送 - 所以我应该如何将我的Query结果返回给主UI线程?
UPD
我已经了解了如何使用代理和事件,但是在提升自定义事件时遇到了问题.我已经声明了一个EventHandler和一个自定义EventArgs:
public delegate void QueryResultEventHandler(object sender, QueryResultEventArgs e);
public class QueryResultEventArgs : EventArgs
{
public List<String> QueryResult { get; set; }
public int QueryRecordsCount { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
在我的DBHelper.class中,我声明了下一个字段和事件:
private QueryResultEventHandler _queryResult;
public event QueryResultEventHandler onQueryResult
{
add
{
lock (this)
{
_queryResult += value;
}
}
remove
{
lock (this)
{
_queryResult -= value;
}
}
}
Run Code Online (Sandbox Code Playgroud)
在UI类(MainWindow)中,我使用下一个代码:
public void GetAllDistricts() {
DBHelper.Instance.onQueryResult += new QueryResultEventHandler(GetAllDistricsResultHandler);
DBHelper.Instance.GetAllDistricts();
}
public void GetAllDistricsResultHandler(object sender, QueryResultEventArgs e){
// Here I'm adding the query result to Table
}
Run Code Online (Sandbox Code Playgroud)
所以我现在的问题是如何异步引发事件?在我的DBHelper.class中,我试图使用带有_query委托的beginInvoke和endInvoke,但似乎我错过了一些代码行,无论它是什么我都无法理解我做错了如何异步引发事件?这是我的DBHelper.class代码:
public void GetAllDistricts() {
try
{
if (_queryResult != null)
{
//** This code should run asynchronously ---------->
using (FbConnection connection = GetConnection())
{
FbCommand getAllDistrictsCommand = new FbCommand();
getAllDistrictsCommand.CommandText = "SELECT * FROM SEND";
getAllDistrictsCommand.Connection = connection;
QueryResultEventArgs args = new QueryResultEventArgs();
using (FbDataReader reader = getAllDistrictsCommand.ExecuteReader())
{
while (reader.Read())
{
//Here must be the processing of query results and filling the
//QueryResultEventArgs
args.QueryResult.Add(reader[0].ToString());
}
args.QueryRecordsCount = reader.GetInt32(reader.GetOrdinal("Rows"));
// And here after sucessfull query I should call OnQueryResult()
OnQueryResult(args);
}
}
//**<--------------------
}
else
{
throw new Exception("...Some exception message...");
}
}
catch (Exception e)
{
log.ErrorException(e.Message, e);
throw new Exception("...Some exception message...");;
}
finally {
CloseConnection();
}
}
// The QueryResultEvent method
protected void OnQueryResult(QueryResultEventArgs e)
{
if (_queryResult != null)
{
_queryResult(this, e);
}
}
Run Code Online (Sandbox Code Playgroud)
首先是关于连接池.如果您将使用ADO.NET,那么您不必担心,因为它已经存在.您不需要做任何额外的工作,只需创建一个连接:
using (var connection = new SqlConnection(connectionString))
{
// Queries to DB
}
Run Code Online (Sandbox Code Playgroud)
您应该始终关闭或处置您的连接.方法的名称看起来"可怕",但实际上连接被重用.请阅读此MSDN文章以获取更多详细信息.
你提出的代码看起来过于复杂.我认为您应该考虑使用async/await模式,它通常不是多线程的,但它处理UI响应问题并简化代码的写入/读取.在较新版本的.NET中,几乎所有可能长时间执行的方法都具有异步版本.例如,您的数据访问层可能看起来像那样(我使用Dapper ORM的 QueryAsync方法只是为了保持代码简洁和简单):
public async Task<IList<District>> GetAllDistrictsAsync()
{
using (var connection = await GetConnectionAsync())
{
return (await connection.QueryAsync<District>("select * from Districts")).ToList();
}
}
public async Task<IDbConnection> GetConnectionAsync()
{
var connectionString =
ConfigurationManager.ConnectionStrings["DbConnectionString"].ConnectionString;
var connection = new SqlConnection(connectionString);
await connection.OpenAsync();
return connection;
}
Run Code Online (Sandbox Code Playgroud)
然后在UI上的某个地方:
private async void Button_Click(object sender, EventArgs e)
{
var districts = await GetAllDistrictsAsync();
}
Run Code Online (Sandbox Code Playgroud)
如果您仍需要在不同的线程中执行某些代码,则应该查看Tasks命名空间.
Task.Factory
.StartNew<IList<District>>(GetAllDistricts)
.ContinueWith(districts =>
{
// UI thread
}, TaskScheduler.FromCurrentSynchronizationContext());
Run Code Online (Sandbox Code Playgroud)
在这个例子GetAllDistricts中不是异步并且在不同的线程中执行.但是ContinueWith会因为在UI线程中执行TaskScheduler.FromCurrentSynchronizationContext().
| 归档时间: |
|
| 查看次数: |
2386 次 |
| 最近记录: |