2017-02-16 22 views
8

Mam .NET rdzeń Web API, które jako warstwę usługi. Warstwa serwisowa ma cały kod EF.najlepsza praktyka korzystania z async czekać w webapi

Jeśli mają basecontroller z tym kodem

protected Task<IActionResult> NewTask(Func<IActionResult> callback) 
{ 
    return Task.Factory.StartNew(() => 
    { 
     try 
     { 
      return callback(); 
     } 
     catch (Exception ex) 
     { 
      Logger.LogError(ex.ToString()); 
      throw; 
     } 
    }); 
} 

w akcji kontrolera I owinąć wszystkie połączenia do służby w powyższej metody np :

[HttpGet("something")] 
public async Task<IActionResult> GetSomething(int somethingId) 
{ 
    return await NewTask(() => 
    { 
     var result = _somethingService.GetSomething(somethingId); 

     if (result != null) 
      return Ok(result); 
     else 
      return NotFound("Role not found"); 
    }); 
} 

Czy to właściwy wzorzec biorąc pod uwagę, że jutro mogę mieć więcej niż jedno wezwanie serwisowe w akcji lub wykonywać połączenia z innymi usługami sieciowymi. Proszę doradź.

+4

Czy możesz podać więcej informacji o tym, co chce osiągnąć tutaj? Twój kod zasadniczo teraz fałszuje pracę asynchroniczną, wykonując pracę synchroniczną w wątku tła. Nie wspominając o używaniu Task.Factory.StartNew jest dość niebezpieczne i powinieneś używać Task.Run zamiast tego, ale nie potrzebujesz nawet Task.Run tutaj. –

+0

coś usługa ma pewne operacje crud, które używają core entityframework. również jutro mogę mieć usługę o nazwie feedService, która będzie miała połączenia, aby pobierać dane z zewnętrznej sieci za pomocą httpclient. Chcę, aby moje api korzystały z async oczekują rzeczy. Powyższe wzorce będą służyły tym potrzebom + wszelkim problemom lub ulepszeniom. – krishna

Odpowiedz

17

chcę mój api korzystać z async czekają thing.does Powyższe wzór posłuży te potrzeby

Nie, to nie. Uruchamianie pracy synchronicznej w puli wątków daje wady asynchronicznego kodu synchronicznego i, z zaletami żadnej z nich.

coś usługa ma pewne operacje CRUD które wykorzystują rdzeń entityframework

Obecnie metoda działania jest to, co nazywam „fałszywy asynchroniczny” - wygląda asynchroniczny (na przykład za pomocą await), ale w rzeczywistości jest po prostu uruchomiony kod blokujący na wątku tła. W ASP.NET, potrzebujesz prawdziwej asynchronii, co oznacza, że ​​musisz być asynchronicznie w drodze. Aby uzyskać więcej informacji o tym, dlaczego jest to złe na ASP.NET, zobacz pierwszą część mojego intro to async on ASP.NET article (dotyczy to głównie nie-core ASP.NET, ale pierwsza część dotycząca synchronicznych i asynchronicznych żądań jest ważna dla każdego rodzaju serwera).

Aby było to naprawdę asynchroniczne, należy rozpocząć od najniższego poziomu - w tym przypadku do wywołań EFCore. Wszystkie obsługują asynchronię. Zastąp więc wywołania API, takie jak x.FirstOrDefault() z await x.FirstOrDefaultAsync() (i tak samo dla wszystkich twoich kreacji/aktualizacji/usunięć itp.).

Następnie pozwól, aby async/await naturalnie się stamtąd wzrośnie; kompilator poprowadzi cię. Skończysz z asynchronicznych metod na swojej somethingService które mogą być spożywane jako takie:

[HttpGet("something")] 
public async Task<IActionResult> GetSomething(int somethingId) 
{ 
    var result = await _somethingService.GetSomethingAsync(somethingId); 
    if (result != null) 
    return Ok(result); 
    else 
    return NotFound("Role not found"); 
} 
+0

Efektywne połączenia asynchroniczne są wolniejsze niż połączenia synchronizujące. – krishna

+0

@ user1603828: Różnica powinna być znikoma. Jeśli to nie jest to, co widzisz, polecam skontaktowanie się z zespołem EF na ten temat. –

+0

Przechodzę przez twój blog ma wiele interesujących informacji. – krishna

6

Dobra, przede wszystkim, należy zaprzestać używania Task.Factory.StartNew i używać Task.Run tylko wtedy, gdy masz ciężką pracę CPU-bound, że cię chcesz uruchomić na wątku puli wątków. W twoim przypadku wcale tego nie potrzebujesz. Należy również pamiętać, że należy używać tylko Task.Run podczas wywoływania metody, a nie w implementacji metody. Możesz przeczytać więcej o tym here.

To, czego naprawdę chcesz w twoim przypadku, to mieć asynchroniczną pracę wewnątrz twojej usługi (nie jestem pewien, czy potrzebujesz nawet usługi w twoim przypadku), kiedy faktycznie dzwonisz do bazy danych i chcesz użyć async/czekaj, a nie uruchamiaj trochę rzeczy na wątku tła.

Zasadniczo usługa powinna wyglądać mniej więcej tak (jeśli jesteś pewien, że potrzebna jest usługa):

class PeopleService 
{ 
    public async Task<Person> GetPersonByIdAsync(int id) 
    { 
     Person randomPerson = await DataContext.People.FirstOrDefaultAsync(x => x.Id == id); 
     return randomPerson; 
    } 
} 

Jak widzisz swoją usługę teraz sprawia asynchroniczny wzywa do bazy danych i że w zasadzie co twój wzór powinien być. Możesz zastosować to do wszystkich swoich operacji (dodaj/usuń/itp.)

Po wykonaniu usługi asynchronicznej powinieneś być w stanie w łatwy sposób wykorzystać dane w akcji.

Twoje działania powinny wyglądać następująco:

[HttpGet("something")] 
public async Task<IActionResult> GetPerson(int id) 
{ 
    var result = await PeopleService.GetPersonByIdAsync(id); 

    if (result != null) 
     return Ok(result); 
    else 
     return NotFound("Role not found"); 
} 
+0

Zrobiłem trochę badań na temat asyncowych metod rdzenia core i powiedziałem, że są wolniejsze niż metody sync ef. radzę. – krishna

+0

@kirshna Metody asynchroniczne SĄ wolniejsze. Mają koszty ogólne, które są nieuniknione podczas ich synchronicznego działania. ALE mogą one zwiększyć przepustowość. Jest więc równowaga pomiędzy przepustowością i wydajnością, które należy wziąć pod uwagę. Jak zawsze w przypadku pytań dotyczących wydajności, jest to skomplikowane. https://www.dotnetrocks.com/?show=1433 Daj to do słuchania, a zobaczysz, o czym mówię. – thinklarge