AutoFac i DbContext has been disposed

0

Witam.
W moich walkach z wdrażaniem wzorca MPV przyszedł czas na Dependenc Injection. No i natrafiłem na taki oto problem:

using Autofac;
using System.Linq;
using System.Diagnostics;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using Model;
using Model.Entities;

namespace DataTest
{
    public class DIResolver
    {
        public static T Resolve<T>()
        {
            ContainerBuilder builder = new ContainerBuilder();

            builder.RegisterType<DbContextMySql>().As<IDbContext>()
                .WithParameter(new NamedParameter("name", "sopdContext"))
                ;
            var container = builder.Build();
            using (var scope = container.BeginLifetimeScope())
            {
                var service = scope.Resolve<T>();

                return service;
            }
        }
    }

    [TestClass]
    public class ContextTest
    {
        [TestMethod]
        public void GetContext()
        {
            IDbContext AppContext = DIResolver.Resolve<IDbContext>();
            var users = AppContext.Users.Select(u => new UserEntity()).ToList();
            foreach(var item in users)
                Trace.TraceInformation("User name :{0}", item.SurName);

        }
    }
}
 

Test rzuca wyjątkiem:

Test Name: GetContext
Test FullName: DataTest.ContextTest.GetContext
Test Source: d:\dev\C#\Workspace_sopd\DataTest\ContextTest.cs : line 35
Test Outcome: Failed
Test Duration: 000,2624654

Result Message:
Test method DataTest.ContextTest.GetContext threw exception:
System.InvalidOperationException: The operation cannot be completed because the DbContext has been disposed.
Result StackTrace:
w System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
w System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)
w System.Data.Entity.Internal.Linq.InternalSet1.Initialize() w System.Data.Entity.Internal.Linq.InternalSet1.get_InternalContext()
w System.Data.Entity.Infrastructure.DbQuery1.System.Linq.IQueryable.get_Provider() w System.Linq.Queryable.Select[TSource,TResult](IQueryable1 source, Expression`1 selector)
w DataTest.ContextTest.GetContext() w d:\dev\C#\Workspace_sopd\DataTest\ContextTest.cs:wiersz 37

Przypuszczam że problem siedzi w konfiguracji wpisu w kontenerze ale niestety googlowe podpowiedzi nic nie dają.
Pomoże ktoś?

1

Problem polega na tym, że using w metodzie Resolve powoduje, że po wywołaniu tej metody container jest usuwany, więc wszystkie obiekty, którymi zarządzał, a więc Twój service również.
Czyli zmienna AppContext już w momencie jej przypisania zawiera DbContext, który zakończył swoją pracę i ma zamknięte połączenie do bazy.

Czemu ma służyć ten kod, który napisałeś? Bo jest on nieźle zagmatwany, i nie ma żadnego sensu.

0

To jest unitTest do sprawdzenia czy w ogóle da się pobrać coś z tabeli Users używając DI

Zmieniłem Resolver na :

    public class DIResolver
    {
        public static T Resolve<T>()
        {
            ContainerBuilder builder = new ContainerBuilder();

            builder.RegisterType<DbContextMySql>().As<IDbContext>()
                .WithParameter(new NamedParameter("name", "sopdContext"))
                .SingleInstance()
                ;
            var container = builder.Build();

            var scope1 = container.BeginLifetimeScope();
            var service1 = scope1.Resolve<T>();

            return service1;

            //using (var scope = container.BeginLifetimeScope())
            //{
            //    var service = scope.Resolve<T>();

            //    return service;
            //}
        }
    }

Wtedy dostaję :

The entity or complex type 'Model.UserEntity' cannot be constructed in a LINQ to Entities query.

0

No tak, bo nie da się przetłumaczyć na język SQL czegoś takiego jak new UserEntity().

0

Przepisywanie kodu z tutoriali o 2:00 w nocy nie sprzyja krytycznej analizie. ;-)
Dzięki. Zmieniłem na :

 
public static T Resolve<T>()
        {
            ContainerBuilder builder = new ContainerBuilder();

            builder.RegisterType<DbContextMySql>().As<IDbContext>()
                .WithParameter(new NamedParameter("name", "sopdContext"))
                .SingleInstance()
                ;
            var container = builder.Build();

            var scope = container.BeginLifetimeScope();
            var service = scope.Resolve<T>();

            return service;
       }

Zmieniłem testową metodę

    [TestClass]
    public class ContextTest
    {

        [TestMethod]
        public void AutoFacContext()
        {
            IDbContext AppContext = DIResolver.Resolve<IDbContext>();
            var users = AppContext.Set<UserEntity>().ToList();
            foreach (var item in users)
                Trace.TraceInformation("User name :{0}", item.SurName);
        }
    }
 

I oczywiście zadziałało. Pytanie dodatkowe. Gdzie w programie desktopowym (WinForms) najlepiej definiować kontener DI i jak powinien być zdefiniowany jeżeli chodzi o modyfikatory dostępu. Chodzi mi o zakres widoczności i tryby dostępu. Gdzieś czytałem, że są wzorce projektowe, wg których warstwa BL w ogóle nie powinna mieć świadomości istnienia kontenera DI. Nie mogę na to powtórnie trafić.

0

Kontener DI to zewnętrzna biblioteka, więc nie rozumiem trochę o co chodzi w pytaniu o jego umiejscowienie i modyfikatory dostępu. Chodzi Ci, gdzie go skonfigurować i uruchomić?
A co do BL i DI, to owszem. Logika biznesowa nie powinna nic wiedzieć, nie tylko o kontenerze DI, ale i o wielu innych bibliotekach. To powinien być praktycznie czysty język.

0

Chodzi mi o podział projektu na warstwy oraz podział solucji na podprojekty. Podział solucji zapewne powinien odpowiadać mniej więcej podziałowi na warstwy. Czy kontener powinien być w osobnym projekcie i jak powinny wyglądać dowiązania.

0

Tu jest dyskusja o dzieleniu solucji na projekty, może Ci coś podpasuje: Architektura aplikacji biznesowych

Kontener może być w oddzielnym module (to się chyba zwie Comoposition Root wtedy), ale ja osobiście preferuję go uruchamiać razem ze startem aplikacji i ładować dynamicznie (bez sztywnych powiązań między projektem startowym a resztą).

Ogólnie nie ma jednego uniwersalnego rozwiązania, trzeba dopasowywać do swoich potrzeb, próbować różnych podejść i zmieniać jeśli okaże się, że pierwotny pomysł był zły.

1 użytkowników online, w tym zalogowanych: 0, gości: 1