【问题标题】:Create one lazy and one eager DbContext via inheritance?通过继承创建一个懒惰和一个渴望的 DbContext?
【发布时间】:2020-06-16 08:55:25
【问题描述】:

我正在使用 EFCore 3.1.5,并且我有一个 DbContext,我希望能够在同一个控制器或服务中使用,无论是懒惰的还是急切的。但是,似乎我无法让它正确加载延迟。 Eager 似乎工作正常。

每当我做一些简单的事情时:

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

每个User 上的每个导航属性都为空。但是,通过预先加载,它可以正常工作:

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) {}
}

这可能是什么问题?我试图在我的控制器/服务中对接口和类进行 IoC。我尝试过使用和不使用services.AddScoped&lt;&gt;()

我想要的只是能够使用惰性 dbContext 或渴望 dbContext,我希望默认使用渴望的 dbContext。

【问题讨论】:

  • 这可能听起来很愚蠢,但请尝试在opt.UseSqlServer(connectionString, x =&gt; x.CommandTimeout(300)); opt.UseLazyLoadingProxies(); 左右切换这些opt.UseLazyLoadingProxies(); opt.UseSqlServer(connectionString, x =&gt; x.CommandTimeout(300));
  • @Seabizkit 不幸的是,这不会有所作为。我知道延迟加载是有效的,因为如果我简单地删除所有 LazyContext 的东西,那么只有 1 个 DbContext,延迟加载就可以完美地工作。
  • 好吧,嗯,我在想你是如何注册它的......你需要通过获取实例services.AddScoped&lt;DbContext&gt;(provider =&gt; provider.GetService&lt;ParadoxCoreContext&gt;());来重用定义的方式,这是我自己的项目,但需要满足你的需求......试试看
  • 本页docs.microsoft.com/en-us/dotnet/api/… 提到可能需要致电services.AddEntityFrameworkProxies()
  • @MortenMoulder 这就是我的意思services.AddScoped&lt;ILazyUserContext, LazyUserContext&gt;((provider =&gt; provider.GetService&lt;LazyUserContext&gt;() ); 你需要告诉你的范围版本中的寄存器使用已经定义的结构,你已经指定了 UseLazyLoadingProxies,这是我的理解,否则@ 987654335@知道如何构造LazyUserContext...

标签: c# entity-framework entity-framework-core ef-core-3.1


【解决方案1】:

解决方案

我想要的只是能够使用惰性 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 路由),或通过类本身。由于此特定设置是特定于类的,并且您不想在 DI 注册中处理它,因此依赖于特定于类的配置方法是有意义的。


为什么您的解决方案不起作用

您的初始方法不起作用的原因是AddDbContext&lt;T&gt;特定的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 类型的依赖,它只会监听第二个注册,而直接忽略第一个。

只有当您的类具有LazyUserContext 的依赖项时,您才会真正获得您在第一次注册时指定的数据库上下文选项。


请注意,当您想要注册默认实现时,可以使用这种特定于类型的注册行为来发挥自己的优势:

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

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

这允许您拥有一些需要专门进行急切/延迟加载的类,以及其他只接受默认值(可能会随着时间而改变)的类。

【讨论】:

  • this.Configuration 不是我班级的财产。您确定 EFCore 3.1.5 中存在这个吗?此外,您的第一部分似乎有复制/粘贴错误。 2x LazyContext 和 2x Enabled = true
  • 这基本上是在加强我对我提出的问题的评论,并且应该是一种让它在不这样做的情况下工作的方法......尽管这没有错。
  • @MortenMoulder:你说得对,我复制了错误的 sn-p。我将 sn-p 更新为 EF Core,但答案的要点基本保持不变。
  • @Flater 而不是 EagerContext,我不能只使用您示例中的 MyContext 吗?
  • @MortenMoulder:是的,假设这是它们之间的唯一区别。虽然为了清楚起见,我通常建议使用两个明确描述的类名,而不是一个被标记为惰性而另一个不被标记为渴望。
【解决方案2】:

问题是由于两个上下文构造函数都使用(依赖)一个相同的选项类型 - DbContextOptions&lt;UserContext&gt;

AddDbContext&lt;TContext&gt;实际上注册了两种类型——上下文本身TContext以及上下文选项依赖的工厂DbContextOptions&lt;TContext&gt;

因此,您正在注册两个选项工厂(以及配置操作)-DbContextOptions&lt;UserContext&gt;DbContextOptions&lt;LazyUserContext&gt;。然而,正如开头提到的LazyUserContext 依赖于DbContextOptions&lt;UserContext&gt;,所以它只是使用第一个选项设置进行实例化,即与另一个完全相同。

这并非特定于延迟加载,而是适用于需要不同选项(不同数据库类型/连接字符串等)的任何场景,这也是泛型类 DbContextOptions&lt;TContext&gt; 存在的原因。

解决办法是改变LazyUserContext的依赖

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

由于基类需要DbContextOptions&lt;UserContext&gt;,因此无法编译,因此将第二个受保护的构造函数添加到仅接受DbContextOptions 的基类中

protected UserContext(DbContextOptions options) : base(options) { }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-02-02
    • 1970-01-01
    • 2016-08-17
    • 2010-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-17
    相关资源
    最近更新 更多