2016-02-13 8 views
5

Szukam właściwego sposobu obsługi wielu wywołań bazy danych, które prawdopodobnie skorzystałyby z jednoczesnego uruchamiania. Kwerendy są tylko do procedur przechowywanych, które są albo wstawia lub łączy przy użyciu danych, które są programowo zestawione w DataTables w mojej aplikacji ASP.NET MVC.Jak poprawnie wykonać asynchroniczne/równoległe wywołania bazy danych?

Oczywiście widziałem pewne informacje na temat async i await, i wydaje się, że to, co musiałbym zrobić, ale nie mam jasnego zrozumienia, jak go wdrożyć. Niektóre informacje mówią, że połączenia będą nadal sekwencyjne i że nadal będzie czekał na inny, aby zakończyć. To wydaje się bezcelowe.

Ostatecznie, chciałbym rozwiązania, które pozwala mi uruchomić wszystkie zapytania w czasie, który zajmuje do zakończenia najdłuższej procedury. Chciałbym, aby wszystkie zapytania zwróciły także liczbę rekordów, których dotyczy (tak jak teraz).

Oto co mam dzieje się teraz (co nie jest w żaden sposób równoległy):

// Variable for number of records affected 
var recordedStatistics = new Dictionary<string, int>(); 

// Connect to the database and run the update procedure 
using (var dbc = new SqlConnection(db.Database.Connection.ConnectionString)) 
{ 
    dbc.Open(); 

    // Merge One procedure 
    using (SqlCommand cmd = new SqlCommand("MergeOneProcedure", dbc)) 
    { 
     // 5 minute timeout on the query 
     cmd.CommandTimeout = 300; 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.Parameters.AddWithValue("@TVP", MergeOneDataTable); 

     // Execute procedure and record the number of affected rows 
     recordedStatistics.Add("mergeOne", cmd.ExecuteNonQuery()); 
    } 

    // Merge Two procedure 
    using (SqlCommand cmd = new SqlCommand("MergeTwoProcedure", dbc)) 
    { 
     // 5 minute timeout on the query 
     cmd.CommandTimeout = 300; 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.Parameters.AddWithValue("@TVP", MergeTwoDataTable); 

     // Execute procedure and record the number of affected rows 
     recordedStatistics.Add("mergeTwo", cmd.ExecuteNonQuery()); 
    } 

    // Merge Three procedure 
    using (SqlCommand cmd = new SqlCommand("MergeThreeProcedure", dbc)) 
    { 
     // 5 minute timeout on the query 
     cmd.CommandTimeout = 300; 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.Parameters.AddWithValue("@TVP", MergeThreeDataTable); 

     // Execute procedure and record the number of affected rows 
     recordedStatistics.Add("mergeThree", cmd.ExecuteNonQuery()); 
    } 

    // Merge Four procedure 
    using (SqlCommand cmd = new SqlCommand("MergeFourProcedure", dbc)) 
    { 
     // 5 minute timeout on the query 
     cmd.CommandTimeout = 300; 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.Parameters.AddWithValue("@TVP", MergeFourDataTable); 

     // Execute procedure and record the number of affected rows 
     recordedStatistics.Add("mergeFour", cmd.ExecuteNonQuery()); 
    } 

    // Merge Five procedure 
    using (SqlCommand cmd = new SqlCommand("MergeFiveProcedure", dbc)) 
    { 
     // 5 minute timeout on the query 
     cmd.CommandTimeout = 300; 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.Parameters.AddWithValue("@TVP", MergeFiveDataTable); 

     // Execute procedure and record the number of affected rows 
     recordedStatistics.Add("mergeFive", cmd.ExecuteNonQuery()); 
    } 

    dbc.Close(); 
} 

return recordedStatistics; 

Wszystkie tego kodu jest w ramach tej samej metody, która montuje dane dla DataTables. Moje ograniczone zrozumienie async doprowadziłoby mnie do przekonania, że ​​będę musiał wyodrębnić poprzedni kod do własnej metody. Nazwałbym tę metodę i powrót. Jednak nie wiem wystarczająco dużo o tym, aby zacząć.

Nigdy wcześniej nie robiłem żadnego kodowania asynchronicznego/równoległego/wielowątkowego. Ta sytuacja sprawia, że ​​czuję, że jest to idealny czas, aby wskoczyć. Powiedziałbym, że chciałbym nauczyć się najlepszego sposobu, zamiast oduczać się w niewłaściwy sposób.

Odpowiedz

7

Oto przykład jak można to zrobić:

Tutaj tworzę dwie metody owinąć dwie operacje, trzeba zrobić to samo dla innych operacji:

public async Task<int> MergeOneDataTableAsync() 
{ 
    // Merge One procedure 
    using (SqlCommand cmd = new SqlCommand("MergeOneProcedure", dbc)) 
    { 
     // 5 minute timeout on the query 
     cmd.CommandTimeout = 300; 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.Parameters.AddWithValue("@TVP", MergeOneDataTable); 

     return await cmd.ExecuteNonQueryAsync().ConfigureAwait(false); 
    } 
} 


public async Task<int> MergeTwoDataTableAsync() 
{ 
    // Merge Two procedure 
    using (SqlCommand cmd = new SqlCommand("MergeTwoProcedure", dbc)) 
    { 
     // 5 minute timeout on the query 
     cmd.CommandTimeout = 300; 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.Parameters.AddWithValue("@TVP", MergeTwoDataTable); 

     return await cmd.ExecuteNonQueryAsync().ConfigureAwait(false); 
    } 
} 

Zauważmy, że Używam metody ExecuteNonQueryAsync do wykonania zapytania.

A potem oryginalna metoda będzie wyglądać następująco:

using (var dbc = new SqlConnection(db.Database.Connection.ConnectionString)) 
{ 
    dbc.Open(); 

    Task<int> task1 = MergeOneDataTableAsync(); 
    Task<int> task2 = MergeTwoDataTableAsync(); 

    Task.WaitAll(new Task[]{task1,task2}); //synchronously wait 

    recordedStatistics.Add("mergeOne", task1.Result); 
    recordedStatistics.Add("mergeTwo", task2.Result); 
} 

Należy pamiętać, że jestem utrzymanie tej metody synchroniczne. Inną opcją (w rzeczywistości lepszy) jest przekształcenie sposobu na asynchroniczny jednego takiego:

public async Task<Dictionary<string, int>> MyOriginalMethod() 
{ 
    //... 
    using (var dbc = new SqlConnection(db.Database.Connection.ConnectionString)) 
    { 
     dbc.Open(); 

     Task<int> task1 = MergeOneDataTableAsync(); 
     Task<int> task2 = MergeTwoDataTableAsync(); 

     int[] results = await Task.WhenAll(new Task<int>[]{task1,task2}); 

     recordedStatistics.Add("mergeOne", results[0]); 
     recordedStatistics.Add("mergeTwo", results[1]); 
    } 

    //... 
    return recordedStatistics; 
} 

Ale to oznaczałoby, że trzeba powołać go asynchronicznie (async all the way).

+0

To wygląda świetnie. Jest to w istocie aplikacja GUI, ale oczekuję, że zablokuje GUI, dopóki zapytania się nie zakończą. Po ich zakończeniu załaduję widok z liczbą rekordów, których dotyczy problem, z każdego z zapytań. Potrzebuję/potrzebuję tych informacji, więc albo zajmuję stronę wzywającą, albo wracam do niej, gdy doczekam jej minuty. Po prostu nie widziałem sensu w odczekaniu 5 minut na jedno zapytanie, jeden po drugim, kiedy mogę poczekać te same 5 minut, aż wszystkie zostaną ukooczone naraz. Dzięki! – FlipperBizkut

+4

Naprawdę masz zakleszczenie czekające na wywołanie 'Task.WaitAll' na zadaniach stworzonych przez' MergeOneDataTableAsync' i 'MergeTwoDataTableAsync', które używają' await' bez 'ConfigureAwait (false)'. –

+0

Czy możesz omówić problem z zakleszczeniem? Być może pokazać sposób na uniknięcie go również? – FlipperBizkut

Powiązane problemy