2015-02-22 16 views
7

Próbuję użyć asynchroniczne, czekać i zadania do realizacji jednego z wymagań asynchronicznych w mojej aplikacji internetowej. Mam poniżej kod w mojej klasie kontrolera. Potrzebuję tylko tego, aby po tym, jak przepływ dotarł do drukowania "kontrolera 1", system uruchomił metodę biznesową w oddzielnym wątku i bez czekania, aż system odpowiedzi dojdzie do polecenia drukowania w kontrolerze i wydrukuje "kontroler 2". Ale kiedy ja wykonując poniższy kod otrzymuję wzorzec wyjściowy jako to-Asynchroniczne zadanie w asp .net MVC 5

controller 1 
PlandesignBS 
controller 2 

Podczas gdy ja spodziewałem this-

controller 1 
controller 2 (Flow should reach here immediately rather than waiting 
       for business methods to get executed First) 

Contoller Class Code

public class PlanDetailsController : Controller 
{  
    [HttpPost] 
    public async Task<ActionResult> Generate(PlanDetailsVM planDetailsVM) 
    { 
     System.Diagnostics.Debug.WriteLine("controller 1"); 
     //calls business method to generate      
     quoteId = await Task.Run(() => _planDesignBS.GenerateBS(planDetailsVM)); 
     System.Diagnostics.Debug.WriteLine("controller 2"); 

     return Json(quoteId , JsonRequestBehavior.AllowGet); 
    } 
} 

działalności Metoda kod -

public int GenerateBS(PandetailsDTO plandetails) 
{ 
    System.Diagnostics.Debug.WriteLine("PlandesignBS"); 

//STEP 1-> call to dataLogic Layer to use Entity Framework 

// STEP 2-> call to a method (this method is part of dll used 
//in our project and we don't have any control on it) 

return quoteId 
} 

EDYCJA: Powodem, dla którego próbuję to zrobić, jest to, że chcę używać kilku żądań postów HTTP z mojej strony klienta. Załóżmy, że wywołuję metodę akcji Generuj najpierw od strony mojego klienta, a następnie nie chcę czekać na jej odpowiedź, a jednocześnie chcę wywołać inny wpis http.

EDYCJA: W tym kod EF, gdzie otrzymuję execption, gdy staram się przestrzegać kilku rozwiązań.Ta metoda zostanie wywołana z mojej metody GenerateBS. Otrzymuję wyjątek na tej linii wewnątrz getSICCode method-

DbContext.Database.ExecuteSqlCommand("spGET_SICCode @industryClasifID,@industrySubClasifID,@SICCode OUTPUT", industryClasifID, industrySubClasifID, SICCode); 

EF Code

public class PlanDesignRepository : Repository<myobject>, IPlanDesignRepository 
{ 
    private IDbSet<myobject> _dbSet; 
    private DbContext _dbContext; 

    private IDbSet<myobject> DbSet 
    { 
     get 
     { 
      if (_dbSet == null) 
      { 
       _dbSet = base.UnitOfWork.Context.Set<myobject>(); 
      } 
      return _dbSet; 
     } 
    } 

    private DbContext DbContext 
    { 
     get 
     { 
      if (_dbContext == null) 
      { 
       _dbContext = base.UnitOfWork.Context; 
      } 
      return _dbContext; 
     } 
    } 
public string GetSICCode(IndustryClassficDO industryClassficDO) 
    { 

     SqlParameter industryClasifID = new SqlParameter("industryClasifID", SqlDbType.Int); 
     industryClasifID.Value = industryClassficDO.IndustryClassificationId; 

     SqlParameter industrySubClasifID = new SqlParameter("industrySubClasifID", SqlDbType.Int); 
     industrySubClasifID.Value = industryClassficDO.IndustrySubClassificationId; 

     SqlParameter SICCode = new SqlParameter("SICCode", SqlDbType.VarChar, 10); 
     SICCode.Direction = ParameterDirection.Output; 

     DbContext.Database.ExecuteSqlCommand("spGET_SICCode @industryClasifID,@industrySubClasifID,@SICCode OUTPUT", industryClasifID, industrySubClasifID, SICCode);    


     return (string)(SICCode.Value); 
    } 
    } 

EDIT: Mam kilka połączeń HTTP POST, jak pokazano below-

AngularJS code for AJAX calls: 

$http({ 
url: key_Url_Generate, 
method: Post, 
params: $scope.PlanDetails 
     }).then(function (result) { 
       //Notify user for success or failure 
            } 
+0

Jak rozporządzać swoją DataContext? – Stilgar

+0

Co należy zrobić, aby go usunąć? –

+0

Powinieneś zadzwonić do metody Dispose, ale nie mówię Ci, żebyś ją wyrzucił. Pytam, gdzie ją wyrzucisz, ponieważ zgłoszony wyjątek mówi, że metoda Dispose została wywołana, ale jakiś kod próbował użyć obiektu po tym. – Stilgar

Odpowiedz

6

Użycie oczekujących oznacza, że ​​czekasz na zakończenie zadania przed kontynuowaniem kodu.Jeśli chcesz, aby wykonać równocześnie z innym kodem należy go zmienić jak to

public class PlanDetailsController : Controller 
{  
    [HttpPost] 
    public async Task<ActionResult> Generate(PlanDetailsVM planDetailsVM) 
    { 
     System.Diagnostics.Debug.WriteLine("controller 1"); 
     //calls business method to generate      
     var task = Task.Run(() => _planDesignBS.GenerateBS(planDetailsVM)); 
     System.Diagnostics.Debug.WriteLine("controller 2"); 

     var quoteId = await task; 
     return Json(quoteId , JsonRequestBehavior.AllowGet); 
    } 
} 

Jednak nie polecam tego jako to niczemu nie służy i może obniżyć wydajność poprzez blokowania puli wątków ASP.NET wątków . Dlaczego mimo to chcesz to zrobić?

+0

Kiedy próbuję zaimplementować twoje rozwiązanie, ponieważ używam Entity Framework w moim kodzie. Otrzymuję błąd DBcontext- "Operacja nie może być zakończona, ponieważ Kontekst DbContext został usunięty. " –

+0

Nie wiem, gdzie tworzysz lub Pozbądź kontekst, więc nie mogę pomóc, chyba że podasz ten kod.Jednak wciąż muszę zapytać, jaki jest twój cel końcowy. Co masz nadzieję osiągnąć, robiąc to? – Stilgar

+0

Powodem, dla którego próbuję to zrobić, jest to, że chcę używać kilku żądań postów HTTP z mojej strony klienta. Chcę wywołać metodę działania Generuj najpierw ze strony mojego klienta, a następnie nie chcę czekać na jej odpowiedź, a jednocześnie chcę wywołać inny wpis http. Postaram się również uwzględnić kod EF w moim pytaniu. –

3

tej linii:

quoteId = await Task.Run(() => _planDesignBS.GenerateBS(planDetailsVM)); 

a synchronicznie czeka, aby zakończyć połączenie wewnątrz Task.Run. Dlatego widzisz "PlandesignBS" drukowane i dopiero po tym widać wezwanie do kontrolera 2.

Jeśli chcesz wystawić „ognia i zapomnij” styl wykonania, rzeczywiście nie chcą czekać co wszystko.

Powinieneś zdecydowanie nie używać Task.Run w ASP.NET w ogóle, as it's dangerous. Zamiast tego, jeśli korzystasz z .NET 4.5.2 lub nowszego, możesz użyć HostingEnvironment.QueueBackgroundWorkItem, a jeśli nie, możesz użyć BackgroundTaskManager Stephana Cleary, który bezpiecznie rejestruje wątek tła z pulą aplikacji, więc twój wątek nie zostanie zakończony raz w re-cycle jest wywoływana:

[HttpPost] 
public ActionResult Generate(PlanDetailsVM planDetailsVM) 
{ 
    System.Diagnostics.Debug.WriteLine("controller 1"); 

    HostingEnvironment.QueueBackgroundWorkItem(ct => _planDesignBS.GenerateBS(planDetailsVM)); 

    System.Diagnostics.Debug.WriteLine("controller 2"); 
    return Json(quoteId , JsonRequestBehavior.AllowGet); 
} 

na marginesie - Jeśli dzwonisz Entity Framework, tam zazwyczaj nie ma potrzeby angażowania dodatkowych wątków. Wersja 6 i wyższa udostępniają interfejsy API oparte na asyncie TAP, które są zwracane w postaci Task, które można wykorzystać do wykonywania takich zapytań opartych na IO.

+0

Na pewno jestem zainteresowany wykorzystaniem ognia i zapomnienia stylu wykonania, ale problemem nie mogę używać HostingEnvironment.QueueBackgroundWorkItem, ponieważ używam niskiej wersji .NET i nie mogę używać żadnego rozwiązania innej firmy. Czy jest jakikolwiek inny sposób robienia tego, który dotyczy mojego przypadku? –

+0

Tak, można ręcznie utworzyć własną implementację za pomocą interfejsu 'IRegisteredObject'. [Tutaj] (http://haacked.com/archive/2011/10/16/the-dangers-of-implementing-recurring-background-tasks-in-asp-net.aspx/) to prosty spacer, przez który można zacznij. Możesz także spojrzeć na implementację ['BackgroundTaskManager'] (https://github.com/StephenCleary/AspNetBackgroundTasks/blob/master/src/AspNetBackgroundTasks/BackgroundTaskManager.cs) –

+0

Próbowałem użyć BackgroundTaskManager, ale otrzymałem związany z EF wyjątek. Podałem szczegóły w pytaniu. Sprawdź, czy możesz w tym pomóc. –

0

Spróbuj tego:

public class PlanDetailsController : Controller 
    {  
     [HttpPost] 
     public async Task<ActionResult> Generate(PlanDetailsVM planDetailsVM) 
     { 
      System.Diagnostics.Debug.WriteLine("controller 1"); 
      //calls business method to generate      
      var quoteIdTask = GenerateAsyncBs(); 

      System.Diagnostics.Debug.WriteLine("controller 2"); 

int id = await quoteIdTask; 

      return Json(quoteId , JsonRequestBehavior.AllowGet); 
     } 
    } 

    private async Task<int> GenerateBsAsync() 
    { 
     //do your planDetails logic here. 
    } 
1

Problem polega na tym, że po prostu nie może wywołać asynchronicznego kodu, które wypadają z asp.net żądanie rurociągu per se.

Funkcja async w asp.net MVC poprawia jedynie wewnętrzną obsługę wątków wewnątrz procesu IIS. NIE umożliwia to zakończenia i zamknięcia odpowiedzi na klienta przed wykonaniem wszystkich operacji.

Jeśli chcesz zrobić prawdziwe rozwiązanie "zapomnij i zapomnij", potrzebujesz więcej.

Niedawno miałem wymóg, aby wiadomości były wysyłane asynchronicznie po przesłaniu formularza w aplikacji internetowej.

Jedynym sposobem znalazłem rozwiązania tego problemu było zastosowanie hangfire:

http://hangfire.io/

+0

Nie chce nawet strzelać i zapominać o rozwiązaniu. W końcu używa wyniku obliczeń asynchronicznych w wyniku akcji. To, co robi, jest po prostu bezużyteczne. – Stilgar

Powiązane problemy