並行

2017-02-22 19 views
10

執行存儲過程我有這2種方法並行

public DataTable GetData1(int Id) 
{ 
    DataTable dt = new DataTable(); 

    using (SqlConnection sqlcon = new SqlConnection(database.Connection.ConnectionString)) 
    { 
     using (SqlCommand cmd = new SqlCommand("spGetData1", sqlcon)) 
     { 
      cmd.CommandType = CommandType.StoredProcedure; 

      cmd.Parameters.Add(new SqlParameter() { ParameterName = "@id", Value = Id}); 

      using (SqlDataAdapter da = new SqlDataAdapter(cmd)) 
      { 
       da.Fill(dt); 
      } 
     } 
    } 

    return dt; 
} 

public DataTable GetData2(int Id) 
{ 
    DataTable dt = new DataTable(); 

    using (SqlConnection sqlcon = new SqlConnection(database.Connection.ConnectionString)) 
    { 
     using (SqlCommand cmd = new SqlCommand("spGetData2", sqlcon)) 
     { 
      cmd.CommandType = CommandType.StoredProcedure; 

      cmd.Parameters.Add(new SqlParameter() { ParameterName = "@id", Value = Id}); 

      using (SqlDataAdapter da = new SqlDataAdapter(cmd)) 
      { 
       da.Fill(dt); 
      } 
     } 
    } 
    return dt; 
} 

,我想在執行一次他們也得到了數據作進一步處理。

我想是這樣

var task1 = Task.Factory.StartNew(() => database.Data.GetData1(1)); 
var task2 = Task.Factory.StartNew(() => database.Data.GetData2(2)); 

var taskList = new List<Task> { task1, task2 }; 

Task.WaitAll(taskList.ToArray()); 

,但在最後一行它

崩潰有一個或多個errors.`

內部異常是

對象refe rence未設置爲對象的實例。

堆棧跟蹤

在System.Threading.Tasks.Task.WaitAll(任務[]任務,的Int32 millisecondsTimeout,的CancellationToken的CancellationToken)

connectionString

System.Data.Entity.DbContext.Database類獲得
public class DatabaseRepository : IDisposable 
    { 
     DbContext dbContext; 

     public DatabaseRepository() 
     { 
      dbContext = new DbContext("connection string ..."); 
      Data = new DataRepository(dbContext.Database); 

     } 
     public DataRepository Data { get; set; } 
} 

enter image description here

但即使我手動設置連接字符串的錯誤是一樣的,所以我不認爲錯誤在這裏。

using (SqlConnection sqlcon = new SqlConnection("connection string ...")) 
    { 
     using (SqlCommand cmd = new SqlCommand("spGetData2", sqlcon)) 
     { 
     ... 
     } 
    } 

我該怎麼做?我看到一些使用Async返回類型的示例,但我不想重複這些方法。

+0

你能說明'數據庫'是如何聲明的嗎?同時訪問它可能並不安全。此外,您收到的完整錯誤消息將有所幫助。請記住,'Task.WaitAll'會阻止你等待你的兩個任務,所以卸載它們並沒有給你帶來太多。你可以把這個方法變成'async'和'await Task.WhenAll' – JSteward

+4

你應該調查'InnerException'你得到的異常,因爲它是一個包裝器'AggregateException'沒有實際的錯誤信息 – VMAtm

+0

你沒有打開你的連接。我願意打賭這是你的例外。 – Cameron

回答

4

database.Connection.ConnectionString的是的靜態字符串否則你不能編譯由於到一個「的對象引用是所必需的非靜態字段,方法或屬性」。

考慮到這一點,它不是連接字符串,因爲其靜態...即使你故意初始化靜態字符串爲null,則錯誤信息會是:

的InnerException = {「ConnectionString屬性尚未初始化。」}

這裏是一個攝製和不能產生錯誤除非你方法的GetData是空的對象:

namespace database 
{ 
public class Program 
{ 
    static void Main(string[] args) 
    { 
     //WORKS!! 
     var repro = new database.Data(); 
     var task1 = Task.Factory.StartNew(() => repro.GetData1(3)); 
     var task2 = Task.Factory.StartNew(() => repro.GetData2(5)); 
     var taskList = new List<Task> { task1, task2 }; 
     Task.WaitAll(taskList.ToArray()); 

     //FAILS WITH ERROR REPORTED!! 
     repro = null; 
     var task1 = Task.Factory.StartNew(() => repro.GetData1(3)); 
     var task2 = Task.Factory.StartNew(() => repro.GetData2(5)); 
     var taskList = new List<Task> { task1, task2 }; 
     Task.WaitAll(taskList.ToArray()); 
    } 
} 

class Data 
{ 
    private string connectionString = "Server=.;Database=CRUD_Sample;Integrated Security=True;Asynchronous Processing = True;"; 
    public DataTable GetData1(int Id) 
    { 
     DataTable dt = new DataTable(); 
     using (SqlConnection sqlcon = new SqlConnection(connectionString)) 
     { 
      using (SqlCommand cmd = new SqlCommand("Get_CustomerbyID", sqlcon)) 
      { 
       cmd.CommandType = CommandType.StoredProcedure; 
       cmd.Parameters.Add(new SqlParameter() { ParameterName = "@id", Value = Id }); 
       using (SqlDataAdapter da = new SqlDataAdapter(cmd)) 
       { 
        da.Fill(dt); 
       } 
      } 
     } 
     return dt; 
    } 

    public DataTable GetData2(int Id) 
    { 
     DataTable dt = new DataTable(); 
     using (SqlConnection sqlcon = new SqlConnection(connectionString)) 
     { 
      using (SqlCommand cmd = new SqlCommand("Get_CustomerbyID", sqlcon)) 
      { 
       cmd.CommandType = CommandType.StoredProcedure; 
       cmd.Parameters.Add(new SqlParameter() { ParameterName = "@id", Value = Id }); 
       using (SqlDataAdapter da = new SqlDataAdapter(cmd)) 
       { 
        da.Fill(dt); 
       } 
      } 
     } 
     return dt; 
    } 
} 
} 

調試

如何找到NullReferenceException的來源?除了查看異常本身 - 關鍵在於將NRE拋出到它發生的位置,然後將鼠標懸停在代碼行上的變量上並查看哪個對象爲空。

+0

謝謝你,這對我在單獨的控制檯項目中工作,但我無法將此解決方案複製到我正在處理的項目..但我沒有看到任何差異:/我試着再次初始化'DataRepository數據= new DataRepository(new System.Data.Entity.DbContext(@「connection string。」)。Database);'並運行'data.GetData1(1)',但它在Task.WaitAll(taskList.ToArray())中崩潰;'線..如果我執行它沒有'任務'塊它的作品。 – Muflix

+0

@Muflix,你有沒有試過Jeremy的調試建議?結果是什麼?哪個對象爲null? – SergGr

+0

在'GetData()'方法中放置BreakPoints,當Task.WaitAll(taskList.ToArray())''調用觸發/執行時,代碼控制(黃色調試行)將停止。然後遍歷每個方法(在Debug> Threads窗口中凍結一個線程以便於調試),然後您將能夠看到'database.Data'對象爲null。爲什麼它是空的我猜是配置,通過一個簡單的示例與EF一起工作,發現差異。 –

1

UPDATE:

Task.WaitAll是造成當前線程阻塞,直到一切都已經完成了。使用Task.WhenAll,以免在等待任務完成時綁定其他線程。

var task1 = Task.Factory.StartNew(() => database.Data.GetData1(1)); 
var task2 = Task.Factory.StartNew(() => database.Data.GetData2(2)); 

var taskList = new List<Task> { task1, task2 }; 

await Task.WhenAll(taskList.ToArray()); 

var result1 = await task1; 
var result2 = await task2; 

Original Answer。 (仍然適用)

根據評論中提供的附加信息,我正在對封裝相關代碼的類進行假設。並行執行時可能會導致NRE,database.Connection超出範圍。在對象的生命週期中提前抽取連接字符串,並在獲取數據時重新使用它。

public class MyDataClass { 

    string connectionString; 
    private Database database; 

    public MyDataClass(DbContext context) { 
     this.database = context.Database; 
     connectionString = database.Connection.ConnectionString; 
    } 

    public DataTable GetData1(int Id) { 
     var dt = new DataTable(); 
     using (var sqlcon = new SqlConnection(connectionString)) { 
      using (var cmd = new SqlCommand("spGetData1", sqlcon)) { 
       cmd.CommandType = CommandType.StoredProcedure; 
       cmd.Parameters.Add(new SqlParameter() { ParameterName = "@id", Value = Id }); 

       using (var da = new SqlDataAdapter(cmd)) { 
        da.Fill(dt); 
       } 
      } 
     } 

     return dt; 
    } 

    public DataTable GetData2(int Id) { 
     var dt = new DataTable(); 
     using (var sqlcon = new SqlConnection(connectionString)) { 
      using (var cmd = new SqlCommand("spGetData2", sqlcon)) { 
       cmd.CommandType = CommandType.StoredProcedure; 
       cmd.Parameters.Add(new SqlParameter() { ParameterName = "@id", Value = Id }); 

       using (var da = new SqlDataAdapter(cmd)) { 
        da.Fill(dt); 
       } 
      } 
     } 
     return dt; 
    } 
} 
+0

謝謝你的回答,我試過了,但是錯誤是一樣的,我更新了這個場景的問題。 – Muflix

+0

@Muflix如果使用'WhenAll'而不是全部等待,它會有所作爲嗎?我相信這是問題的根源。等待全部導致其他線程阻塞。 – Nkosi

+0

我將代碼更改爲'Task.WhenAll(taskList.ToArray());'並且它不會拋出任何錯誤,但在數據庫分析器中我看不到任何數據庫命中。當我改變代碼爲'await Task.WhenAll(taskList.ToArray());'出現以下錯誤'await運算符只能在異步方法中使用。考慮使用async修飾符標記此方法,並將其返回類型更改爲Task ',因爲我的存儲庫過程不返回任務對象。 – Muflix