2015-12-16 13 views
5

I eine WebAPI Controller haben, die Dienste injiziert durch Autofac im OWIN Startup KlasseEF + Autofac + async "wird der aktuelle Zustand der Verbindung verbindet"

builder.Register(c => new MyEntities()).InstancePerRequest(); 

Ich habe auch versucht

builder.Register(c => new MyEntities()).InstancePerLifetimeScope(); 

hat In einer Controller-Aktion rufe ich eine Service-Methode auf, um einen neuen Datensatz zu erstellen. Übergeben Sie die erstellte ID an eine externe API über HttpClient, um weitere Daten zu erhalten, und aktualisieren Sie dann den neuen Datensatz mit einigen Rückgabedaten.

[HttpPost, Route("")] 
public async Task<IHttpActionResult> MyControllerAction(MyModel model) 
{ 
    var id = await _MyService.CreateNewThing(model.SomeId); 
    var externalData = await CallExternalApiThroughHttpClient(id); 
    await _MyService.UpdateNewThing(id, externalData); 
    return Ok(); 
} 

Servicecode

public class MyService : IMyService 
{ 
    private MyEntities _context; 

    public MyService(MyEntities context) 
    { 
     _context = context; 
    } 

    public async Task<int> CreateNewThing(int someId) 
    { 
     var thing = new Thing 
     { 
      SomeId = someId 
     }; 

     _context.Things.Add(thing); 

     await _context.SaveChangesAsync(); 

     return thing.Id; 
    } 

    public async Task UpdateNewThing(int id, string externalDataField) 
    { 
     var thing = await _context.Things.SingleOrDefaultAsync(o => o.Id == id); 

      if (thing == null) 
      { 
       throw new ServiceNotFoundException("Thing " + transactionId + " not found"); 
      } 

      thing.ExternalDataField= externalDataField; 

      await _context.SaveChangesAsync(); 
    } 
} 

Aber ich bekomme eine InvalidOperationException in UpdateNewThing var thing = await _context.Things.SingleOrDefaultAsync(o => o.Id == id);

System.InvalidOperationException: The connection was not closed. The connection's current state is connecting. 

Es scheint, wie ich entweder Injektion der Kontext aufgeben müssen, async/await oder etwas verwenden wie eine Kontextfactory; es sei denn, jemand kann etwas Einfaches entdecken, das ich übersehen habe, das würde mich mit diesem Design fortfahren lassen.

+0

Welche Zeile löst die Ausnahme aus? Können Sie zeigen, wie Sie '_context' in den Container injizieren? Gibt es einen Ort, an dem Sie über den Kontext verfügen? Rufen Sie 'CreateNewThing' und' UpdateNewThing' von derselben Methode auf? Können Sie einen vollständigeren Code zeigen? –

+0

Hinzugefügt mehr Code. Ich injiziere den Kontext im Serviceklassenkonstruktor. Ich bin nirgends entsorgt. CreateNewThing und UpdateNewThing befinden sich beide in derselben WebApi-Aktion. Ich brauche die neue ID, um an die externe API zu senden, und muss dann den Datensatz mit einigen Rückgabedaten aktualisieren; normalerweise würde ich meinen Dienst nicht so nennen. –

+1

Ich sehe kein Problem in Ihrem Code. Was passiert, wenn Sie den Web-Service-Aufruf nicht durchführen und stattdessen mit gefälschten 'externalData'-Daten testen? –

Antwort

1

Ihr Code sieht gut aus in einem Single-Thread-Kontext. Allerdings ist DbContext nicht threadsicher, und ich vermute, was passiert, wenn Sie CreateNewThing() auf einem Thread ausführen, und der Taskplaner in diesem Fall UpdateNewThing() auf einem anderen Thread ausführt.

So oder so, eine bessere Metapher ist einen Kontext Fabrik zu verwenden, die Sie in Ihre IMyService in diesem Fall zu injizieren, und dann für jede IMyService Methode Sie einen neuen MyEntities Kontext in einem using() Block erstellen.

DbContext 's sind billig zu erstellen und so sollen sie verwendet werden; langlebige Kontexte sind fast immer falsche Verwendung.

Bearbeiten 1 - Beispiel Kontext Fabrik wie angefordert. Ich tendiere dazu, eine generische Fabrik zu implementieren, die mehrere Kontexte erstellen kann, aber das geht wahrscheinlich über den Rahmen dieser Frage hinaus.

public interface IMyEntitiesFactory 
{ 
    MyEntities Create(); 
} 

public class MyEntitiesFactory : IMyEntitiesFactory 
{ 
    MyEntities IMyEntitiesFactory.Create() 
    { 
     return new MyEntities(); 
    } 
} 

// For use with unit tests; e.g. pass a mock object to the constructor. 
public class TestMyEntitiesFactory : IMyEntitiesFactory 
{ 
    private readonly MyEntities _value; 

    public TestMyEntitiesFactory(MyEntities value) 
    { 
     _value = value; 
    } 

    MyEntities IMyEntitiesFactory.Create() 
    { 
     return _value; 
    } 
} 
+0

Danke, das ist, was ich vermutete, aber ich würde lieber nicht async verwenden, als es mit einem halben Verständnis dessen zu verwenden, was es tut. Ich habe ein paar Beispiele für eine Kontextfactory gesehen; alle anders. Haben Sie ein Beispiel für eines, das Sie vor –

+0

verwendet haben? Es gibt viele Beispiele, weil Sie es ziemlich viel tun können, aber Sie möchten, solange es einen Kontext erstellt. Ich habe der Antwort ein ziemlich einfaches Beispiel hinzugefügt. – sellotape

+0

ok, also nur eine einfache Fabrik - kein schickes EF-Zeug für SaveChanges. Danke –

Verwandte Themen