簡體   English   中英

最佳實踐:C#使用DB

[英]Best Practices: C# working with DB

首先,我是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();
      }
    }
}

在某些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();
}

在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; }
}

在我的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和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);
  }
}

首先是關於連接池。 如果您將使用ADO.NET,那么您不必擔心,因為它已經存在。 您不需要做任何額外的工作,只需創建一個連接:

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

您應該始終關閉處置您的連接。 方法的名稱看起來“可怕”,但實際上連接被重用。 請閱讀此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;
}

然后在UI上的某個地方:

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

如果您仍需要在不同的線程中執行某些代碼,則應該查看Tasks命名空間。

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

在此示例中, GetAllDistricts不是異步並且在不同的線程中執行。 但是由於TaskScheduler.FromCurrentSynchronizationContext() ContinueWith將在UI線程中執行。

public void GetAllDistricts() {

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

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

    }

但是你將面臨的問題是你將無法從EventHandler訪問你的UI控件,因為它將被拒絕,因為你不再在同一個線程中......

請參閱該文章以獲得一些解釋

如何從C#中的另一個線程更新GUI?

為避免這種情況,您可以使用BackgroundWorker控件。

使用此選項

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

它易於使用,易於數據庫操作,代碼更少。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM