2013-02-17 11 views
12

mam regulatora:DbContext zostały usunięte i autofac

private readonly ILogger _logger;  
private readonly IRepository _repository; 

public HomeController(ILogger logger, IRepository repository) 
{ 
    _logger = logger; 
    _repository = repository; 
} 

ta jest repozytorium:

public class EfRepository : IRepository 
{ 
    // ...methods for add, delete, update entities 
    // .... 

    public void Dispose() 
    { 
     if (this._context != null) 
     { 
      this._context.SaveChanges(); 
      (this._context as IDisposable).Dispose(); 
      this._context = null; 
     } 
    } 
} 

Wreszcie rodzaje rejestracji w IoC:

_builder.RegisterType<Logger>().As<ILogger>(); 
_builder.RegisterType<EfRepository>().As<IRepository>().WithParameter("context", new PcpContext()); 

Uruchomienie wniosek otrzymuję ten błąd:

The operation cannot be completed because the DbContext has been disposed.

Próbowałem zmienić rejestracyjny EfRepository tak:

_builder.RegisterType<EfRepository>() 
    .As<IRepository>() 
    .WithParameter("context", new PcpContext()).InstancePerLifetimeScope(); 

W tym przypadku pierwsze żądanie wykończenie, ale kiedy próbuje otworzyć inne strony, pojawia się błąd ponownie. Gdzie jest problem?

+3

NIGDY nie przesyłaj naszego DbContext do dyspozycji. Dispose zostanie wywołany w przypadku wyjątku, ale nie chcesz zapisywać żadnych zmian, kiedy to się stanie. – Steven

+0

@Steven: Usunąłem tę linię, w każdym razie nie rozwiązuje to problemu. – user1260827

+1

Za każdym razem, gdy widziałem SaveChanges wewnątrz dispose, programista miał problemy. Zamierzam jeszcze zrozumieć, jaki wzorzec projektu sugeruje lub poleca "zatwierdza" lub "zapisuje" w Dispose. Zamierzam zasugerować, abyś ponownie ocenił plan, aby zapisać w środku. A co z obsługą błędów. dlaczego kojarzysz "zatwierdzanie" zmian z bazą danych ze zbiorem śmieci? Warto przeczytać http://msdn.microsoft.com/en-us/library/fs2xkftw%28VS.80%29.aspx Kiedy uruchamiasz dispose? może zacznij tutaj: http://stackoverflow.com/questions/898828/c-sharp-finalize-dispose-pattern –

Odpowiedz

19

Podczas korzystania z metody WithParameter instancja parametru będzie taka sama dla każdego rozstrzygniętego obiektu. Tak więc z .WithParameter("context", new PcpContext()) efektywnie używasz tego samego wystąpienia klasy PcpContext dla dowolnego rozwiązanego wystąpienia IRepository.

Przy obecnym kodzie, po usunięciu instancji IRepository, będzie ona również usuwać tę instancję PcpContext. Następnie każda kolejna próba rozwiązania IRepository otrzyma instancję PcpContext, która została usunięta. Potrzebujesz sposobu na otrzymanie nowego, świeżego egzemplarza DbContext EF na każdym Żądaniu HTTP, które jest usuwane na końcu żądania.

Jedną z opcji może być zarejestrowanie blok kodu dla IRepository tak że blok kodu jest wykonywana za każdym razem, gdy IRepository musi zostać rozwiązany:

_builder.Register<IRepository>(c => new EfRepository(new PcpContext())) 

Lepszym rozwiązaniem byłoby stworzenie nowego IDatabaseContext abstrakcji, aktualizowanie EfRepository, więc zależy to od nowej abstrakcji IDatabaseContext zamiast klasy PcpContext (co może już mieć miejsce :)).

Klasa implementacji dla IDatabaseContext będzie twoją klasą PcpContext, która musi dziedziczyć z EF DbContext i prawdopodobnie odbierać ciąg połączenia jako parametr.

public class EfRepository : IRepository 
{ 
    private readonly IDatabaseContext _context; 

    public EfRepository(IDatabaseContext context) 
    { 
     _context = context; 
    } 

    ...methods for add, delete, update entities 

    //There is no longer need for this to be disposable. 
    //The disaposable object is the database context, and Autofac will take care of it 
    //public void Dispose() 
} 

public interface IDatabaseContext : IDisposable 
{ 
    ... declare methods for add, delete, update entities 
} 

public class PcpContext: DbContext, IDatabaseContext 
{ 
    public EntityFrameworkContext(string connectionString) 
     : base(connectionString) 
    { 
    } 

    ...methods exposing EF for add, delete, update entities 

    //No need to implement IDisposable as we inherit from DbContext 
    //that already implements it and we don´t introduce new resources that should be disposed of 
} 

Jest to lepsze dzięki pomysłowi wykorzystania kontenera IoC i pozostawienia im ciężaru zarządzania na całe życie. Teraz twoja klasa Repository nie musi być jednorazowa, ani też nie może zarządzać i pozbywać się zależności IDatabaseContext. To Autofac będzie śledził instancję kontekstu i usuwał ją w razie potrzeby.

Z tego samego powodu prawdopodobnie będziesz chciał użyć InstancePerLifetimeScope z zależnościami kontekstu bazy danych. Oznaczałoby to, że ten sam kontekst EF jest współdzielony dla każdej instancji repozytorium na tym samym żądaniu Http i jest usuwany na końcu żądania.

_builder.RegisterType<EfRepository>() 
    .As<IRepository>(); 

_builder.RegisterType<PcpContext>() 
    .As<IDatabaseContext>() 
    .WithParameter("connectionString", "NameOfConnStringInWebConfig") 
    .InstancePerLifetimeScope(); 
+0

Nie używam Autofac, więc to było interesujące przeczytać ... +1 dobra analiza i wyjaśnienie –

+0

Dzięki @soadyp ! Używam Unity zamiast Autofac. Chociaż zasady są takie same, szczegóły na temat tego, jak każdy kontener implementuje zarządzanie cyklem życia będzie inny. (Na przykład w przypadku Unity używałbym okresu Hierarchichal dla 'IDatabaseContext' w połączeniu z nowym kontenerem podrzędnym utworzonym na żądanie HTTP) –

+0

Co się stanie, jeśli mam wiele ograniczonych kontekstów DbContext? Zarejestruj DbContext jako kluczowany? – Chazt3n

0

Poszedłem z prostym rozwiązaniem "bloku kodu", jak sugerował @Daniel J.G (lambda).

Poniżej przykładowy kod tego w Autofac. Przykład Danielsa jest dla Unity, jak również wspomina o sobie.Ponieważ PO dodał Autofac jako znak to wydawało mi się istotne:

_builder.Register(c => new AppDbContext()).As(typeof(AppDbContext)); 

Ten kod rozwiązał problem miałem DbContext has been disposed mający z Entity Framework. Zauważ, że w porównaniu do większości innych pojemników DI - w tym Unity - Autofac przełącza się wokół zarejestrowanej rzeczy i rzeczy, do której się odnosi.

Dla przykładu kodu podanego przez PO poprawka byłoby coś takiego:

_builder.Register(c => new EfRepository(new PcpContext())).As(IRepository); 

Zauważ, że ten ostatni bit jest kod niesprawdzone. Ale powinieneś odnieść się do odpowiedzi Danielsa, aby uzyskać więcej informacji, ponieważ myślę, że ma rację z "lepszą opcją". Ale możesz użyć mojej opcji rozwiązania, jeśli nie masz teraz czasu (jak ja). Po prostu dodaj TODO, aby móc zarabiać na technicznym długu, który ponosisz :).

Kiedy to zrobię, zobaczę, czy mogę zaktualizować tę odpowiedź z działającym kodem dla Autofac, który jest zgodny z jego "lepszą opcją". Najpierw chcę uważnie przeczytać this article. Po szybkim przeczytaniu wydaje mi się, że ludzie Autofac promują za pomocą "Service Locator" do obsługi zakresu życia. Ale według Marka Seemanna to jest an anti-pattern, więc mam trochę rzeczy do wymyślenia ... Każdy ekspert DI z opinią?

Powiązane problemy