2017-01-24 1 views
1

Ich versuche, einen selbst gehosteten Webdienst zu erstellen, der seinen Server mit Microsoft.Owin.Hosting.WebApp.Start<NotifyStartup>("http://localhost:9000/") startet und einen von System.Net.Http.ApiController abgeleiteten Controller enthält. NotifyStartup sieht wie folgt aus:Aufgabe zurückgegeben von IHttpActionResult.ExecuteAsync() Überladung wird nicht ausgeführt

using System.Web.Http; 
... 
[Route("notify")] 
[HttpPost] 
public IHttpActionResult Notify([FromBody]object body) 
{ 
    return new HttpAction(); 
} 

hier HttpAction:

using System.Net.Http; 
using System.Web.Http; 
using System.Threading; 
using System.Threading.Tasks; 
... 
public class HttpAction : IHttpActionResult 
{ 
    public HttpAction() { } 

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) 
    { 
     return new Task<HttpResponseMessage>(() => 
     { 
      var rspContent = "here's a response string"; 
      var rsp = new HttpResponseMessage(System.Net.HttpStatusCode.OK); 
      if (!string.IsNullOrEmpty(rspContent)) 
      { 
       rsp.Content = new StringContent(rspContent); 
      } 
      return rsp; 
     }, cancellationToken); 
    } 
} 

(An einem gewissen Punkt HttpAction Aufmerksamkeit auf Notify() zahlen ‚s body Parameter

using System.Web.Http; 
using Owin; 
... 
class NotifyStartup 
{ 
    public void Configuration(IAppBuilder appBuilder) 
    { 
     var config = new HttpConfiguration(); 
     config.MapHttpAttributeRoutes(); 

     appBuilder.UseWebApi(config); 
    } 
} 

Die Steuerung dieses URI-Handler hat und rspContent wird etwas zugewiesen werden, das einige Datenbanksuchen erfordert, weshalb ich es versuche ing diese Arbeit asynchron zu machen.)

Wenn ich das Programm und POST ausgeführt http://localhost:9000/notify/ die URI-Handler aufgerufen wird, erstellt er eine HttpAction Instanz, und dass ExecuteAsync() der Instanz-Methode aufgerufen wird. Die Aufgabe, die es zurückgibt, wird jedoch nie ausgeführt, und der Client reagiert nicht mehr auf eine Antwort. Wenn ich ExecuteAsync() ändern, so dass die Arbeit wird synchron gemacht und die Antwort zurück in einer Hülle Aufgabe:

var rspContent = "here's a response string"; 
var rsp = new HttpResponseMessage(System.Net.HttpStatusCode.OK); 
if (!string.IsNullOrEmpty(rspContent)) 
{ 
    rsp.Content = new StringContent(rspContent); 
} 
return Task.FromResult(rsp); 

die Wrapper Aufgabe ist Lauf und der Kunde erhält seine Antwort.

Soweit ich weiß, sollten die von new Task<>... und Task.FromResult() erstellten Aufgaben identisch mit dem Anrufer aussehen. Warum wird es await (oder was auch immer es tatsächlich tut, um das Ergebnis zu erhalten) eins und nicht das andere? Was mache ich falsch? Ist es möglich, dass dies funktioniert?

Antwort

0

die von new Task<> und Task.FromResult() erstellt Aufgaben sollten identisch mit dem Anrufer suchen.

Sie sehen aus der Aufruferperspektive identisch aus, aber sie sind aus der Implementierungsperspektive nicht identisch.

Der Task Konstruktor startet die Aufgabe nicht, deshalb sollten Sie sie nicht verwenden. Statt Task.Run verwendet, die eine heiße Aufgabe zurück:

public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) 
{ 
    return Task.Run(() => 
    { 
     var rspContent = "here's a response string"; 
     var rsp = new HttpResponseMessage(System.Net.HttpStatusCode.OK); 
     if (!string.IsNullOrEmpty(rspContent)) 
     { 
      rsp.Content = new StringContent(rspContent); 
     } 
     return rsp; 
    }, cancellationToken); 
} 

Obwohl ich behaupten würde, dass dies durch selbst überflüssig sein kann, da eine Aktion in WebAPI selbst bereits auf einem Thread-Pool-Thread lief, ist es in der Regel überflüssig wickle es in einem zusätzlichen Thread herum.

+0

Da ist etwas, das ich nicht wusste, wusste ich nicht, und ein anderer Grund, nicht zu mögen .NET. Aus C++ kommend ging ich davon aus, dass der Zeitpunkt und der Zeitpunkt, an dem eine Aufgabe (future) ausgeführt wird, der Laufzeit entsprach und dass das Aufrufen des Ergebnisses ausreichend war, um sicherzustellen, dass es synchron oder asynchron ausgeführt wird. Da ich hier nur die explizite Einplanung in den Thread-Pool wählen kann, wäre es wahrscheinlich besser für mich, nur sicherzustellen, dass alle I/O-gebundenen Aufrufe, die ich mache, asynchron sind und darauf verzichten, ein aktuelles Thread-Runnable zu erstellen Aufgabe. –

+0

@EvanBurkitt Wenn Ihre Operation IO-gebunden ist, müssen Sie 'Task.Run' überhaupt nicht verwenden, Sie müssen sich [" async-await "] ansehen (https://msdn.microsoft.com/en- us/library/mt674882.aspx) und API verwenden, die selbst asynchrone Endpunkte bereitstellen, z. B. 'HttpClient' für asynchrone HTTP-Aufrufe. –

+0

ja, das habe ich gemeint. Ich sollte Async-Funktionen aufrufen, anstatt Aufgaben zu erstellen. Async-Programmierung in C# scheint eine seltsame Mischung aus Low-Level zu sein (Entscheidung darüber, wie und wann Tasks ausgeführt werden müssen explizit gemacht werden) und High-Level (magische Fortsetzungen, die durch den 'await'-Operator erzeugt werden). Ich bin mir nicht sicher, ob es mir gefällt, aber ich werde mich daran gewöhnen. Ich schätze Ihre Hilfe. –

Verwandte Themen