2012-04-17 20 views
6

Mam dość dużą aplikację sieci Web korzystającą z LINQ-TO-SQL działającą na platformie Azure i doświadczam przejściowych błędów z SQL-Azure, w związku z czym muszę wdrożyć ponowne próby. Jestem świadomy Transient Fault Handling Framework i kilku miejsc, które dają przykłady, jak go używać, ale wygląda na to trzeba owinąć każdy zapytań LINQ w coś podobnego do tego:Ponów logikę dla LINQ-TO-SQL na SQL Azure - Wydajne implementacje?

RetryPolicy retry = new RetryPolicy<MyRetryStrategy>(5, TimeSpan.FromSeconds(5)); 
Result = retry.ExecuteAction(() => 
{ 
    … LINQ query here... 
}); 

z setkami zapytań LINQ w mojej warstwie danych wydaje się to naprawdę nieładne, a także fakt, że wiele razy zapytanie nie jest faktycznie wykonywane, dopóki wyniki nie zostaną wyliczone. Na przykład większość moich funkcji w mojej warstwie danych zwraca warstwę IQueryable <> do warstwy biznesowej (co czyni je bardziej elastycznymi niż przywracanie listy). Oznaczałoby to, że musisz zaśmiecić warstwę logiki biznesowej za pomocą logiki bazy danych - brzydka.

Sądzę więc, że aby zachować logikę ponowną w warstwie danych, musiałbym wstawić .ToList() do wszystkich moich zapytań, więc są one wykonywane bezpośrednio, a nie w powyższej warstwie.

Naprawdę szkoda, że ​​nie było sposobu implementacji logiki ponownej w niektórych klasach bazowych i nie trzeba zmieniać wszystkich moich zapytań. Wygląda na to, że EF również będzie miał ten problem.

Czy to jest prawdziwa odpowiedź, aby spróbować i porozmawiać z zespołem SQL-Azure o automatycznych próbach, więc nie musimy się tym martwić w naszym kodzie?

Odpowiedz

0

Nie jestem świadomy dobrego rozwiązania, ponieważ LINQ do SQL nie pozwala nam przechwytywać kwerend. Ale mały refaktor kodu może pomóc. Coś jak (pseudo kod):

public Result QueryWithRetry(IQueryable query) 
{ 
     RetryPolicy retry = new RetryPolicy<MyRetryStrategy>(5, TimeSpan.FromSeconds(5)); 
    (() => 
    { 
     return retry.ExecuteAction(query); 
    } 
} 

Teraz jest nieco łatwiej wywołać tę metodę:

wynik = QueryWithRetry (... LINQ kwerendy tutaj ...);

Jednak wciąż należy zmodyfikować kod i zmienić każde zapytanie.

Pozdrawiam,

Ming Xu.

+0

To trochę pomaga, ale czy nadal nie powinienem się martwić o zapytania, które faktycznie nie zostaną wykonane później w kodzie, w którym są wyliczane? Dlatego musiałbym naprawdę zbadać każde zapytanie i dowiedzieć się, gdzie faktycznie jest wywoływana baza danych. – PeteShack

+0

Przepraszam, nie rozumiem cię bardzo dobrze. Czy masz na myśli, że martwisz się, jeśli coś pójdzie nie tak, będzie trudniej znaleźć źródło problemu, ponieważ zawijam zapytanie wewnątrz struktury próbnej? W tym przypadku chciałbym zaproponować, aby ustawić punkt przerwania na ponowną próbę.ExecuteAction i przejść przez kod. –

+0

Nie, po prostu dodawanie logiki ponownej próby do istniejącej aplikacji zawierającej setki zapytań będzie trudnym zadaniem - głównie dlatego, że wywołanie bazy danych niekoniecznie musi się zdarzyć w kwerendzie linq-sql w warstwie danych. Wykonanie kwerendy może być opóźnione do momentu, gdy IQueryable zostanie faktycznie wyliczone - co może się zdarzyć w warstwie biznesowej. Po prostu nie wydaje się być czystym rozwiązaniem tego problemu. – PeteShack

1

Po zrobieniu czegoś takiego, poszedłem do przodu i zrobiłem z niego bibliotekę: https://github.com/daveaglick/LinqToSqlRetry (MIT jest licencjonowany i dostępny na NuGet).

Można ponowić SubmitChanges() połączeń pisząc SubmitChangesRetry() zamiast:

using(var context = new MyDbContext()) 
{ 
    context.Items.InsertOnSubmit(new Item { Name = "ABC" }); 
    context.SubmitChangesRetry(); 
} 

Można również ponowić zapytania przy użyciu metody Retry() rozszerzenia:

using(var context = new MyDbContext()) 
{ 
    int count = context.Items.Where(x => x.Name == "ABC").Retry().Count(); 
} 

Specyficzna logika ponawiania jest kontrolowany przez politykę. Pod maską, mechanizm ponawiania wygląda następująco:

int retryCount = 0; 
while (true) 
{ 
    try 
    { 
     return func(); 
    } 
    catch (Exception ex) 
    { 
     TimeSpan? interval = retryPolicy.ShouldRetry(retryCount, ex); 
     if (!interval.HasValue) 
     { 
      throw; 
     } 
     Thread.Sleep(interval.Value); 
    } 
    retryCount++; 
} 

Zrozum, że funkcja w wywołaniu func() a obiektem retryPolicy są świadczone na podstawie użytkowania. To daje ci pojęcie o tym, co się dzieje podczas pętli retry. Wystarczy spojrzeć do repozytorium, aby uzyskać więcej informacji.