最佳实践:C#使用DB

时间:2015-04-06 01:57:59

标签: c# multithreading database-connection

首先,我是Java程序员,我是C#的新手,我需要C#开发人员的意见。我正在开发一个连接到数据库(firebird 1.5)的应用程序,查询一些数据并返回给我,所以没有什么可复杂的,但遗憾的是我已经陷入了一些困境:

我们知道数据库连接应该在单独的线程中实现,因为它是一个高权重操作,并且所有连接都应该在连接池中,以便重用已经打开的连接而不是创建新连接。

所以这里是我的第一个问题 - 如何正确组织连接池? (连接池怎么样我读过通常连接池已经由数据提供者实现了,我可以在连接参数中设置它,就像“connectionBuilder.Pooling = true;”)

查询怎么样?我的意思是我总是使用每个线程的查询(我认为这是正确的,因为我们也做了一个高权重的操作,我错了吗?无论如何,我很高兴看到你组织数据库工作的最佳实践< / strong>)在Java中我只是通过使用接口匿名类来返回来自单独线程的查询结果:

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类中(对于eaxample 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 中我试图使用带有_query委托的beginInvoke&amp; 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);
  }
}

3 个答案:

答案 0 :(得分:8)

首先关于连接池。如果您将使用ADO.NET,那么您不必担心,因为它已经存在。您不需要做任何额外的工作,只需创建一个连接:

using (var connection = new SqlConnection(connectionString))
{
    // Queries to DB
}

您应始终关闭处理您的连接。方法的名称看起来“可怕”,但实际上连接被重用。请阅读此MSDN article以获取更多详细信息。

您提出的代码看起来过于复杂。我认为你应该考虑使用async/await模式,它通常不是多线程的,但它处理UI响应问题并简化代码的编写/读取。在较新版本的.NET中,几乎所有可能长时间执行的方法都具有异步版本。例如,您的数据访问层可能看起来像那样(我使用Dapper ORM's 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;
}

然后在UI上的某个地方:

private async void Button_Click(object sender, EventArgs e)
{
    var districts = await GetAllDistrictsAsync();
}

如果你仍然需要在不同的线程中执行一些代码,你应该看看Tasks namespace。

Task.Factory
    .StartNew<IList<District>>(GetAllDistricts)
    .ContinueWith(districts =>
    {
        // UI thread
    }, TaskScheduler.FromCurrentSynchronizationContext());

在此示例中,GetAllDistricts不是异步并且在不同的线程中执行。但由于ContinueWithTaskScheduler.FromCurrentSynchronizationContext()将在UI线程中执行。

答案 1 :(得分:1)

public void GetAllDistricts() {

        DBHelper.Instance.onQueryResult += 
                  new QueryResultEventHandler(GetAllDistricsResultHandler);

       new Thread(
           new ThreadStart(DBHelper.Instance.GetAllDistricts)
            ).Start();

    }

但是您将遇到的问题是您无法从EventHandler访问您的UI控件,因为它将被拒绝,因为您不再在同一个线程中...

请参阅该文章以获得一些解释

How to update the GUI from another thread in C#?

为避免这种情况,您可以使用BackgroundWorker控件。

答案 2 :(得分:-1)

使用此选项

http://www.asp.net/mvc/overview/older-versions-1/models-(data)/creating-model-classes-with-the-entity-framework-cs

它易于使用,易于数据库操作,代码更少。