2015-12-16 28 views
5

Ho un controllore WebAPI che offre servizi immessi per autofac nella classe OWIN avvioEF + autofac + asincrono "stato attuale della connessione è la connessione"

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

Ho anche provato

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

In un'azione del controller, chiamo un metodo di servizio per creare un nuovo record, passare l'id creato su una API esterna tramite HttpClient per ottenere altri dati, quindi aggiornare il nuovo record con alcuni dati di ritorno.

[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(); 
} 

codice di servizio

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(); 
    } 
} 

ma ottengo un 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. 

Sembra che devo rinunciare a nessuna iniezione contesto, async/attendono o usare qualcosa come un contesto; a meno che qualcuno non riesca a individuare qualcosa di semplice che mi sia sfuggito mi avrebbe permesso di continuare con questo disegno.

+0

Quale riga genera l'eccezione? Puoi mostrare come si inietta '_context' nel contenitore? C'è un posto dove ti godi del contesto? Chiami 'CreateNewThing' e' UpdateNewThing' dallo stesso metodo? Puoi mostrare un codice più completo? –

+0

Aggiunto altro codice. Inietto il contesto nel costruttore della classe di servizio. Non sono disposto da nessuna parte. CreateNewThing e UpdateNewThing sono entrambi nella stessa azione WebApi. Ho bisogno che il nuovo ID venga inviato alla API esterna e quindi devo aggiornare il record con alcuni dati di ritorno; normalmente non chiamerei il mio servizio in questo modo. –

+1

Non riesco a vedere un problema nel codice. Cosa succede se non si effettua la chiamata al servizio web e si verifica invece con alcuni dati 'externalData' falsi? –

risposta

1

Il codice sembra buono in un contesto a thread singolo. Tuttavia, DbContext non è thread-safe e sospetto che stia succedendo che stai eseguendo CreateNewThing() su un thread e che in questo caso l'utilità di pianificazione delle operazioni esegua UpdateNewThing() su un thread diverso.

In entrambi i casi, una metafora migliore è quello di utilizzare una fabbrica di contesto, che si inietta nel vostro IMyService in questo caso, e poi per ogni metodo IMyService si crea un nuovo contesto MyEntities in un blocco using().

DbContext 's sono economici per creare e questo è come sono destinati ad essere utilizzati; i contesti a vita lunga sono quasi sempre un uso scorretto.

Modifica 1 - esempio di fabbrica di contesto come richiesto. Tendo a implementare uno stabilimento generico in grado di creare più contesti, ma probabilmente si sta spostando al di fuori dello scopo di questa domanda.

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

Grazie, questo è quello che sospettavo, ma preferirei non usare async piuttosto che usarlo con una mezza comprensione di ciò che sta facendo. Ho visto alcuni esempi di un contesto; tutto diverso. Hai un esempio di quello che hai usato prima di –

+0

Ci sono molti esempi perché puoi farlo in modo molto carino, a prescindere dal fatto che crei un contesto. Ho aggiunto un esempio abbastanza semplice alla risposta. – sellotape

+0

ok, quindi solo una fabbrica semplice - niente cose fantasiose di EF per SaveChanges. Grazie –