【发布时间】:2015-04-06 01:57:58
【问题描述】:
首先,我是一名 Java 程序员,而且我是 C# 新手,我需要 C# 开发人员的意见。我正在开发一个连接到数据库的应用程序(firebird 1.5),查询一些数据并返回给我,所以没有什么复杂的,但不幸的是我遇到了一些事情:
我们知道数据库连接应该在单独的线程中实现,因为它是一个重量级的操作,所有的连接都应该在连接池中,以便重用已经打开的连接,而不是创建新的连接。
那么我的第一个问题来了 - 如何正确组织连接池? (我读过的连接池呢,通常连接池已经由数据提供者实现,我可以在连接参数中设置它,比如“connectionBuilder.Pooling = true;”)
查询呢?我的意思是我一直使用 Query per-Thread (我认为这是正确的,因为我们也执行高权重操作,我错了吗?无论如何我很高兴看到您组织数据库工作的最佳实践) 并且在 Java 中,我只是通过使用 interfaces 和 anonymous classes 从单独的线程返回查询结果,如下所示:
在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();
}
}
}
在一些 UI 类中(例如 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();
}
在 C# 中,我应该使用委托来标记哪个方法将在线程中执行,但我不能将任何回调作为参数发送 - 所以 我应该如何将我的查询结果返回到主 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; }
}
在我的 DBHelper.class 中,我声明了下一个字段和事件:
private QueryResultEventHandler _queryResult;
public event QueryResultEventHandler onQueryResult
{
add
{
lock (this)
{
_queryResult += value;
}
}
remove
{
lock (this)
{
_queryResult -= value;
}
}
}
在 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
}
所以我现在的问题是如何异步引发事件?在我的 DBHelper.class 中,我正在尝试将 beginInvoke&endInvoke 与 _query 委托一起使用,但似乎我错过了一些代码行,无论是什么我都无法理解我做错了什么以及如何提高事件异步?这是我的 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);
}
}
【问题讨论】:
-
只有在架构允许的情况下,数据库连接池才会真正有益。如果您正在开发客户端服务器类型的应用程序,即直接连接到服务器的前端,那么连接池有点忽略了这一点。您是否直接从客户端应用程序连接到数据库。
-
@Namphibian 是的,我正在开发一个客户端-服务器类型的应用程序并直接连接到数据库。
-
在您的情况下,添加连接池只会增加复杂性而没有任何好处。通常,连接池将位于应用程序服务器上。要获得连接池的好处,您需要迁移到 n 层架构。
-
@Mike 回答了更新主 UI 线程的异步后台线程,对于您的另一个问题:如何正确组织连接池? 更常见的连接池不是数据提供者或编码具体,它与数据库有关。例如,SQL Server 当连接字符串相同时自动处理Connection Pooling。
标签: c# multithreading database-connection