MVC 5 с Ninject и EF Core, контекст выдает исключение после одного запроса

Я пытаюсь использовать EF Core в нашем веб-приложении ASP.Net MVC 5 с помощью Framework 4.6.1. Мы используем Ninject для внедрения зависимостей. Я вставляю контекст в слой DAL. Мне удалось выполнить один запрос, но контекст выдает следующее исключение при выполнении следующего запроса

Для этого DbContext не настроен поставщик базы данных. Поставщика можно настроить, переопределив метод DbContext.OnConfiguring или используя AddDbContext в поставщике службы приложения. Если используется AddDbContext, также убедитесь, что ваш тип DbContext принимает объект DbContextOptions в своем конструкторе и передает его в базовый конструктор для DbContext.

Я видел эту ошибку раньше, но я не совсем уверен, как она применима в моей ситуации. Что именно в моей настройке вызывает эту ошибку? Вот код:

Контроллер:

public ActionResult GetEnrollmentWorkflows()
{
    var class1Entities = this._class1Service.GetAll().ToList();
    var class1EntityIds = class1Entities.Select(x => x.Class1EntityId).ToList(); // Works
    var class2Entity =
        this._class2EntityService
            .GetByClass1EntityIds(class1EntityIds).ToList(); // Fails with exception
    return PartialView("_somePartial", model);
}

Привязка контекста:

kernel.Bind<Context.ContextTenant>().ToSelf().InTransientScope()
    .WithConstructorArgument("options",
        new DbContextOptionsBuilder<Tables.ContextTenant>()
    .UseSqlServer(ConfigurationManager.ConnectionStrings
        [EnvironmentConsts.DbConnectionTenant].ConnectionString)
    .Options);

Контекст:

public ContextTenant(DbContextOptions<Tables.ContextTenant> options) : base(options)
{}

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    base.OnConfiguring(optionsBuilder);
}

Модули Ninject:

public class DalModule : NinjectModule
{
    public override void Load()
    {
        this.Kernel.Bind(x =>
            x.FromThisAssembly().SelectAllClasses().Join
                .FromAssemblyContaining<ISomeInterface>()
                .SelectAllInterfaces()
                .BindAllInterfaces().Configure(y => y.InTransientScope()));
    }
}



public class ServiceModule : NinjectModule
{
    public override void Load()
    {
        this.Kernel?.Load(new[] { new DalModule() });
        this.Kernel.Bind(x =>
            x.FromThisAssembly().SelectAllClasses().Join
                .FromAssemblyContaining<ISomeInterface>()
                .SelectAllInterfaces()
                .BindAllInterfaces().Configure(y => y.InTransientScope()));
    }
}

Классы DAL

Класс1:

public class Class1Repository : IClass1Repository
{
    private ContextTenant ContextTenant { get; }

    public Class1Repository(ContextTenant contextTenant)
    {
        ContextTenant = contextTenant;
    }

    public IEnumerable<Class1Entity> GetAll()
    {
        return this.ContextTenant.Class1Entity.Select(x => x);
    }
}

2 класс:

public class Class2Repository : IClass2Repository
{
    private readonly ContextTenant _contextTenant;

    public Class2Repository(ContextTenant contextTenant)
    {
        _contextTenant = contextTenant;
    }

    public IEnumerable<Class2Entity> GetByClass1EntityIds(
        IEnumerable<int> class1EntityIds)
    {
        return this._contextTenant.Class2Entity.Where(x =>
            class1EntityIds.Contains(x.Class1EntityId));
    }
}

Услуги:

public class Class1Service : IClass1Service
{
    private readonly IClass1Repository _class1Repository;

    public Class1Service(IClass1Repository class1Repository)
    {
        _class1Repository = class1Repository;
    }

    public IEnumerable<Class1Entity> GetAll()
    {
        return this._class1Repository.GetAll();
    }
}

public class Class2Service : IClass2Service
{
    private readonly IClass2Repository _class2Repository;

    public Class2Service(IClass2Repository class2Repository)
    {
        _class2Repository = class2Repository;
    }

    public IEnumerable<Class2Entity> GetByClass1EntityIds(
        IEnumerable<int> class1EntityIds)
    {
        return this._class2Repository.GetByClass1EntityIds(class1EntityIds);
    }
}

Изменить: вот еще немного кода с самого начала на случай, если это что-то повлияет:

[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(NinjectWebCommon), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(NinjectWebCommon), "Stop")]
namespace MyApp
{
    public static class NinjectWebCommon
    {
        private static readonly Bootstrapper bootstrapper = new Bootstrapper();

        public static void Start()
        {
            DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
            DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
            bootstrapper.Initialize(CreateKernel);
        }

        public static void Stop()
        {
            bootstrapper.ShutDown();
        }

        private static IKernel CreateKernel()
        {
            // Bind the context
        }
    }
}

person Thomas927    schedule 23.08.2018    source источник


Ответы (2)


Основываясь на том, что я читаю выше, я считаю, что это потому, что вы используете RequestScope. Дело в том, что контекст db будет удаляться после каждого использования, и поскольку вы все еще находитесь в том же запросе, когда вы вызываете его снова, он был удален, но все же он обслуживает тот же экземпляр. Измените его на Transient (или эквивалент Transient с Ninject, что означает получение нового контекста db при каждом запросе), и это должно решить проблему.

person Bagzli    schedule 24.08.2018
comment
Я только что попробовал TransientScope, и он по-прежнему ведет себя так же. Я заменил весь RequestScope на TransientScope. - person Thomas927; 24.08.2018
comment
@ Thomas927 Вы на 100% уверены, что ошибка не изменилась? - person Bagzli; 24.08.2018
comment
Да, ошибка вообще не изменилась; Я сравнил журналы между сегодняшним и вчерашним днями - person Thomas927; 24.08.2018
comment
@ Thomas927 Судя по приведенному выше коду, я не вижу ничего плохого. Я предполагаю, что приведенный выше код - это не ваш фактический производственный код, а больше / меньше псевдокода. Я бы предложил создать рабочую демонстрацию на github с созданием вашей ошибки, на которую мы можем взглянуть, в противном случае я не думаю, что смогу помощь. В любом случае, по крайней мере, вы захотите сохранить свой контекст db на Transient vs Request, даже если он не является причиной этой проблемы. - person Bagzli; 24.08.2018
comment
Вышеупомянутый в основном тот же код, только изменили имена. Я обновил вопрос, добавив еще немного кода на случай, если при запуске ninject есть что-то, что вызывает эту ошибку. Я, вероятно, попытаюсь воспроизвести это и дам ссылку, если сделаю это. - person Thomas927; 24.08.2018
comment
@ Thomas927 в привязке контекста указано область запроса, может быть, вы пропустили это во время тестирования? - person Bagzli; 24.08.2018
comment
Я не менял исходный код, который задавал в вопросе. Я просто изменил его на своей машине разработчика и протестировал. Вы видите что-то в коде, который я добавил при редактировании, что ограничивало бы RequestScope? Я только добавил код для NinjectWebCommon - person Thomas927; 24.08.2018
comment
Позвольте нам продолжить это обсуждение в чате. - person Bagzli; 24.08.2018

У меня были подобные проблемы в прошлом, которые иногда можно было решить, изменив

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    base.OnConfiguring(optionsBuilder);
}

In

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    if (optionsBuilder.IsConfigured == false)
    { 
        base.OnConfiguring(optionsBuilder);
    }
}
person JRB    schedule 06.09.2018
comment
Я вроде как подтвердил, что это не имеет значения. Другое приложение, над которым я работаю, также страдает от этой проблемы. Он был создан всего 6 месяцев назад, поэтому в нем не так много унаследованных вещей. Насколько я помню, я удалил эту проверку, потому что установленная там точка останова никогда не выполнялась, поэтому переопределение этого метода не имело смысла. Я оставил его там на случай, если окончательное решение потребует этого переопределения. - person Thomas927; 12.09.2018