Создать один ленивый и один нетерпеливый DbContext через наследование? - PullRequest
2 голосов
/ 16 июня 2020

Я использую EFCore 3.1.5, и у меня есть DbContext, который я хочу использовать в том же контроллере или службе, ленивый или нетерпеливый. Однако, похоже, я не могу заставить его правильно загружать ленивый. Кажется, нетерпеливый работает нормально.

Когда бы я ни делал что-то простое, например:

var users = await _lazyDbContext
    .Users
    .Take(10)
    .ToListAsync();

, каждое свойство навигации для каждого User имеет значение null. Однако при активной загрузке все работает нормально:

var users = await _dbContext
    .Users
    .Include(x => x.Contact)
    .Take(10)
    .ToListAsync();

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<LazyUserContext>((sp, opt) =>
        {
            var connectionString = "very secret";
            opt.UseSqlServer(connectionString, x => x.CommandTimeout(300));
            opt.UseLazyLoadingProxies();
        });

    services.AddDbContext<UserContext>((sp, opt) =>
        {
            var connectionString = "very secret";
            opt.UseSqlServer(connectionString, x => x.CommandTimeout(300));
        });

    services.AddScoped<IUserContext, UserContext>();
    services.AddScoped<ILazyUserContext, LazyUserContext>();
}

UserContext.cs

public interface IUserContext
{
    DbSet<User> Users { get; set; }

    DbSet<Contact> Contacts { get; set; }
}

public class UserContext : DbContext, IUserContext
{
    public UserContext(DbContextOptions<UserContext> options) : base(options) {}

    public DbSet<User> Users { get; set; }

    public DbSet<Contact> Contacts { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Users>(e => 
        {
            e.HasOne(x => x.Contact).WithOne(x => x.User).HasForeignKey(x => x.ContactId);
        }
    }
}

LazyUserContext.cs

public interface ILazyUserContext : IContext {}

public class LazyUserContext : UserContext, ILazyUserContext
{
    public LazyUserContext(DbContextOptions<UserContext> options) : base(options) {}
}

В чем может быть проблема? Я попытался использовать Io C как интерфейс, так и класс в моем контроллере / службе. Я пробовал с services.AddScoped<>() и без него.

Все, что мне нужно, это возможность использовать ленивый dbContext или нетерпеливый dbContext, где я хочу по умолчанию использовать нетерпеливый.

Ответы [ 2 ]

2 голосов
/ 16 июня 2020

Решение

Все, что мне нужно, это возможность использовать ленивый dbContext или нетерпеливый dbContext

Вы должны иметь возможность просто установить конфигурацию в подклассах:

public class LazyContext : MyContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseLazyLoadingProxies();
    }
}

public class EagerContext : MyContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {

    }
}

Существует два варианта настройки конфигурации параметра db: через конструктор (и, следовательно, маршрут DI) или через сам класс. Поскольку этот конкретный параметр является специфичным для класса c, и вы не хотите манипулировать им при регистрации DI, имеет смысл полагаться на метод конфигурации c, специфичный для класса.


Почему ваше решение не сработало

Причина, по которой ваш первоначальный подход не сработал, заключается в том, что AddDbContext<T> регистрирует , в котором c T указан тип вашей зависимости. Из источника:

Метод расширения AddDbContext по умолчанию регистрирует типы DbContext с ограниченным временем жизни.

Обратите внимание, что он регистрирует тип контекста, а не какие-либо интерфейсы / предки этот тип контекста.

Итак, когда вы это сделаете:

services.AddDbContext<LazyUserContext>((sp, opt) =>
{
    var connectionString = "very secret";
    opt.UseSqlServer(connectionString, x => x.CommandTimeout(300));
    opt.UseLazyLoadingProxies();
});

services.AddScoped<ILazyUserContext, LazyUserContext>();

Если ваш класс имеет зависимость типа ILazyUserContext, он прослушивает только вторую регистрацию, а Flatout игнорирует первую .

Только когда ваш класс имеет зависимость LazyUserContext, вы действительно получите параметры контекста базы данных, указанные при первой регистрации.


Обратите внимание, что вы можете использовать этот тип с указанием c поведение регистрации в ваших интересах, когда вы хотите зарегистрировать реализацию по умолчанию:

// Specific interface => specific type
services.AddScoped<IUserContext, UserContext>();
services.AddScoped<ILazyUserContext, LazyUserContext>();

// General interface => explicitly chosen default type
services.AddScoped<IContext, UserContext>();

Это позволяет вам иметь некоторые классы, требующие особой нетерпеливой / ленивой загрузки, и другие классы, которые просто принимают независимо от того, что является значением по умолчанию (которое может измениться со временем).

1 голос
/ 16 июня 2020

Проблема вызвана тем, что оба конструктора контекста используют (зависят от) один и тот же тип опций - DbContextOptions<UserContext>.

AddDbContext<TContext> фактически регистрирует два типа - сам контекст TContext а также фабрика для зависимости опций контекста DbContextOptions<TContext>.

Итак, вы регистрируете две фабрики опций (вместе с действием конфигурации) - DbContextOptions<UserContext> и DbContextOptions<LazyUserContext>. Однако, как упоминалось в начале, LazyUserContext зависит от DbContextOptions<UserContext>, поэтому он просто создается с первой настройкой параметров, то есть точно так же, как и другие.

Это не c для отложенной загрузки , но для любого сценария, который требует других параметров (другой тип базы данных / строка подключения et c.) и является причиной существования generi c class DbContextOptions<TContext>.

Решение состоит в том, чтобы изменить LazyUserContext dependency

public LazyUserContext(DbContextOptions<LazyUserContext> options) : base(options) { }

, и поскольку это не будет компилироваться, потому что база ожидает DbContextOptions<UserContext>, добавьте второй защищенный конструктор к базовому классу, принимающему только DbContextOptions

protected UserContext(DbContextOptions options) : base(options) { }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...