Создание EF Core DbContexts - PullRequest
       38

Создание EF Core DbContexts

0 голосов
/ 11 июля 2019

У меня странная проблема с EF Core, и я не могу понять, почему ...

public class Startup
{
    static Config Config;

    public Startup(IConfiguration configuration)
    {
        Config = new Config();
        configuration.Bind(Config);
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpContextAccessor();
        services.AddScoped(ctx => ctx.GetService<IHttpContextAccessor>()?.HttpContext);
        services.AddScoped(ctx => ctx.GetService<HttpContext>()?.Request);
        services.AddAuthInfo();
        services.AddSingleton(Config);

        services.AddDbContext<MembershipDataContext>(options => options.UseSqlServer(Config.Connections["Membership"]));
        services.AddDbContext<CoreDataContext>(options => options.UseSqlServer(Config.Connections["Core"]));
        services.AddDbContext<B2BDataContext>(options => options.UseSqlServer(Config.Connections["B2B"]));

        services.AddScoped<IMembershipDataContext, MembershipDataContext>();
        services.AddScoped<ICoreDataContext, CoreDataContext>();
        services.AddScoped<IB2BDataContext, B2BDataContext>();

        ...

... Я извлекаю пользовательскую информацию аутентификации из каждого запроса и вставляю ее в мои DbContexts.

Из-за процесса инициализации у меня был CTOR, который принимает DbContextOptions, поэтому я просто добавил секунду в надежде, что он вызовет правильный ...

public EFDataContext(DbContextOptions options, IAuthInfo auth) : base(options) { AuthInfo = auth; }

public EFDataContext(DbContextOptions options) : base(options) { }

... Во время выполнения я вижу, как оба CTOR попадают несколько раз в один запрос (не то, что я ожидал).

Из других постов я отмечаю, что многие говорят, что мне не нужны последние три строки, но удаление их дает мне исключения, говорящие мне, что другие объекты больше не могут быть построены DI.

Так что я в замешательстве ...

Как мне заставить это работать так, чтобы я мог создать только 1 экземпляр на запрос и только ударить CTOR с наибольшим количеством параметров, когда я это делаю?

Ответы [ 2 ]

0 голосов
/ 11 июля 2019

Вы должны , а не добавлять свои контексты отдельно в качестве области. Это основной источник вашей проблемы. Когда вы используете AddDbContext, он регистрирует ваш класс DbContext. Если вы продолжаете запрашивать интерфейс, он не знает, как удовлетворить этот интерфейс; он только знает, как удовлетворить класс. Вот почему вы получаете исключения, и именно поэтому вы чувствовали необходимость добавить отдельные регистрации интерфейса. Однако, когда вы делаете , что , вы запрашиваете другой экземпляр вашего контекстного класса, так как это совершенно другая регистрация.

Вам не нужен интерфейс для вашего контекста. Я вижу, что люди делают это все время, и это просто ошеломляет. Здесь абсолютно нулевая точка. Единственное, что должно быть общедоступно в вашем контексте, это ваши DbSet свойства и встроенные методы, такие как Add, SaveChangesAsync и т. Д. Если вы хотите или должны работать с DbSet в общем, есть Set<T> для этого, так что ваш класс контекста может быть просто введен напрямую. Кроме того, не похоже, что у вас будет несколько реализаций этого контекста. Это представление вашей базы данных, и как таковое, это то, что есть: одна реализация на все времена.

Кроме того, вы не должны вводить что-либо в свой контекст, кроме DbContextOptions. Если вам нужна услуга, у DbContext есть внутренний поставщик услуг, и это то, что вы должны использовать, т.е. this.GetService<IAuthInfo>.

UPDATE

Судя по ветке комментариев, я не думаю, что мне было понятно, что я предлагал. Вы должны иметь что-то вроде:

public interface IService
{
    void DoSomething();
}

Затем вы создадите реализации this для каждого из ваших провайдеров, например CosmosDbService, RavenDbService, EFService и т. Д. Затем в EFService вы добавляете свой контекстный класс, не интерфейс :

public class EFService : IService
{
    private readonly MyDbContext _context;

    public EFService(MyDbContext context)
    {
        _context = context;
    }

    public void DoSomething()
    {
        // use _context
    }
}

Таким образом, контекст только там ради EF. Там нет ничего постороннего, и там нет никакой бизнес-логики. Повсюду в вашем коде вы вводите IService, а затем добавляете в соответствующую реализацию поставщика (EF, Cosmos, Raven и т. Д.) Через контейнер DI. Вся ваша бизнес-логика живет в классе обслуживания, как и должно быть, и вам не нужен интерфейс для вашего контекста.

Это также, вероятно, означает, что вам также не нужны посторонние услуги, такие как IAuthInfo в вашем контексте, то есть вы можете избежать внутреннего поставщика услуг. Вместо этого вы вводите IAuthInfo в свой класс обслуживания.

То, что вы делаете сейчас, является полной противоположностью. Вы принципиально запутываете свою бизнес-логику с EF, заставляя контекст реализовывать интерфейс, не имеющий ничего общего с его целью: служение простой единицей работы.

0 голосов
/ 11 июля 2019

Итак, я понял, что если я изменю заводской код, то смогу нормально обрабатывать конструкцию во время сценариев «настройки» для БД.

Это позволило мне иметь только один CTOR в контексте, который янужно, но чистый результат заключается в том, что мне все еще нужны эти дополнительные строки, или происходит сбой с исключением DI ...

Невозможно разрешить службу для типа 'IMyContext' при попытке активировать 'SomeOther type'.

Затем он щелкнул ... Я никогда не спрашиваю "MyDbContext", я всегда спрашиваю "IMyDbContext" ... дополнительные строки должны сопоставить вызовы интерфейса в контейнере со строго типизированным контекстом, а не«заново добавить контекст» в контейнер.

...