为什么 IConfiguration 对象注入 DbContext 后为 null? [ASP.NET 核心 3.1]
Why is the IConfiguration object null after injecting it to DbContext? [ASP.NET Core 3.1]
在发布这个问题之前,我浏览了多个相似的帖子。我的问题集中在“尽管应用了常见且可能正确的解决方案,但为什么会发生这种情况?”。
我正在开发 .NET Core 3.1 网络应用程序。我有一个名为 'SkipQContext' 的 DbContext。我正在尝试使用 Configuration
对象从 SkipQContext
文件中的 appsettings.json
访问连接字符串。
为此,我将 IConfiguration
作为服务注入 SkipQContext
构造函数。
构造函数:
private readonly string ConnectionString;
public SkipQContext()
{
}
public SkipQContext(DbContextOptions<SkipQContext> options, IConfiguration configuration)
: base(options)
{
ConnectionString = configuration.GetConnectionString("DefaultConnection");
}
我也在ConfigureServices方法中注册了Startup
class。
services.AddSingleton(Configuration);
现在,当我在我的存储库 classes 之一中实例化 SkipQContext
时,会调用 SkipQContext
的默认构造函数。当我尝试使用它获取数据时,我得到“IConfiguration 对象为空。”
我在 ConfigureServices
方法中应用了断点,可以看到 IConfiguration
对象具有连接字符串值。
我的第一个问题是,当我在 ConfigureServices
中注册它并在 SkipQContext
构造函数中注入它时,为什么它在 SkipQContext
中为空?网上有多个答案表明这是正确的方法。
此外,我在想,我可能没有正确实例化 SkipQContext
。正如我的声明:
SkipQContext db = new SkipQContext();
命中 SkipQContext
的默认构造函数,它是空的,而不是注入 IConfiguration
的重载构造函数。
P.S。如果最后一个问题是愚蠢的。我对 .NET Core 中的依赖注入是如何工作的还是有点不清楚。
无需将配置实例化为单例,WebHost 的默认构建器已经在请求中注入配置,您的启动 Class 应该如下所示
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
string conn = Configuration.GetConnectionString("NAME OF YOUR CONNECTION STRING IN THE application.json FILE");
services.AddDbContext<CLASSOFURDBCONTEXT>(config =>
{
config.UseSqlServer(conn);
});
}
}
并且您的 dbcontext 应该具有以下构造函数
public YourDbContext(DbContextOptions<YourDbContext> options) : base(options)
{
}
那么您只需要在控制器或服务中调用 DbContext,DI 将完成剩下的工作
根据您的 IConfiguration 抛出空引用异常的原因,我可以想到 2 种可能性。要么你需要做另一种实例化,就像这样
services.AddSingleton<IConfiguration,Configuration>();
或者可能是因为您没有在 DbContext 本身中使用 DI,所以您不需要执行 new YourContextDbContext()
。你应该只是简单地将它放在服务或控制器的构造函数中,它应该“神奇地”工作,而你实际上不需要创建它的实例。
Also, I am thinking, I might not be instantiating the SkipQContext
rightly. As my statement:
SkipQContext db = new SkipQContext();
hits the default constructor of SkipQContext
which is empty and not the overloaded constructor where IConfiguration
is injected.
你是对的,这不是依赖注入的工作方式。每当您执行 new Something
时,您就会 明确地 绕过依赖注入。关于依赖注入的要点是,具有依赖关系的组件(例如数据库上下文)不需要自己创建该依赖关系,甚至 知道 如何自己创建该依赖关系。
当您调用 new SkipQContext()
时,您 是 显式创建该依赖关系,因此您 紧密地 耦合到 SkipQContext
以及上下文正常工作所需的任何内容(在本例中,它需要 DbContextOptions
和 IConfiguration
)。相反,您想要的是组件对其依赖项 loosely coupled 。因此,您只需声明您 需要 的依赖项,并要求某人或其他人为您完成这些依赖项。这正是依赖注入的用武之地:
有了依赖注入,你就有了一个“依赖注入容器”,它负责创建你或某些组件可能需要的所有依赖。您在 Startup.ConfigureServices
的中央位置配置容器,然后您可以通过服务的构造函数简单地声明您需要的依赖项。但是为了让容器向该服务提供这些依赖项,服务必须由由容器本身创建。
所以你将看到的是,你基本上必须通过依赖注入来消耗所有内容。但这也使您在不使用依赖注入时很容易意识到:每当您编写 new Something
时,容器就不会创建某些东西,因此不会自动实现其依赖关系。取决于那东西可能是你想要的,也可能不是(例如,创建一个 List<string>
或一个 DTO 对象是你想直接做的事情,创建一个服务或具有其他依赖关系的东西可能不是).
回到您的问题,为了让 DI 容器处理 SkipQContext
的构造函数中的依赖项,您必须让 DI 容器为您创建该上下文。所以你不能用 new
创建它,而是你必须 depend 将它添加到你需要它的任何组件的构造函数中。
例如如果你有一个控制器,只需将它添加为依赖项:
public class HomeController : Controller
{
private readonly SkipQContext _db;
public HomeController(SkipQContext db)
{
_db = db;
}
public async Task<IActionResult> Index()
{
var items = await _db.Items.ToListAsync();
return View(new IndexViewModel
{
Items = items,
});
}
}
关于您的数据库上下文的最后一点说明:如果您使用 DI 容器 正确地 注册数据库上下文,那么它已经使用获取的 DbContextOptions
配置传递给构造函数。这些选项还将包括上下文打开数据库连接所需的连接字符串。所以你不需要手动传递 IConfiguration
或在那里提取连接字符串。它会由 EF Core 自动为您完成。
正确的上下文设置可能如下所示(在 ConfigureServices
中):
services.AddDbContext<SkipQContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
在发布这个问题之前,我浏览了多个相似的帖子。我的问题集中在“尽管应用了常见且可能正确的解决方案,但为什么会发生这种情况?”。
我正在开发 .NET Core 3.1 网络应用程序。我有一个名为 'SkipQContext' 的 DbContext。我正在尝试使用 Configuration
对象从 SkipQContext
文件中的 appsettings.json
访问连接字符串。
为此,我将 IConfiguration
作为服务注入 SkipQContext
构造函数。
构造函数:
private readonly string ConnectionString;
public SkipQContext()
{
}
public SkipQContext(DbContextOptions<SkipQContext> options, IConfiguration configuration)
: base(options)
{
ConnectionString = configuration.GetConnectionString("DefaultConnection");
}
我也在ConfigureServices方法中注册了Startup
class。
services.AddSingleton(Configuration);
现在,当我在我的存储库 classes 之一中实例化 SkipQContext
时,会调用 SkipQContext
的默认构造函数。当我尝试使用它获取数据时,我得到“IConfiguration 对象为空。”
我在 ConfigureServices
方法中应用了断点,可以看到 IConfiguration
对象具有连接字符串值。
我的第一个问题是,当我在 ConfigureServices
中注册它并在 SkipQContext
构造函数中注入它时,为什么它在 SkipQContext
中为空?网上有多个答案表明这是正确的方法。
此外,我在想,我可能没有正确实例化 SkipQContext
。正如我的声明:
SkipQContext db = new SkipQContext();
命中 SkipQContext
的默认构造函数,它是空的,而不是注入 IConfiguration
的重载构造函数。
P.S。如果最后一个问题是愚蠢的。我对 .NET Core 中的依赖注入是如何工作的还是有点不清楚。
无需将配置实例化为单例,WebHost 的默认构建器已经在请求中注入配置,您的启动 Class 应该如下所示
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
string conn = Configuration.GetConnectionString("NAME OF YOUR CONNECTION STRING IN THE application.json FILE");
services.AddDbContext<CLASSOFURDBCONTEXT>(config =>
{
config.UseSqlServer(conn);
});
}
}
并且您的 dbcontext 应该具有以下构造函数
public YourDbContext(DbContextOptions<YourDbContext> options) : base(options)
{
}
那么您只需要在控制器或服务中调用 DbContext,DI 将完成剩下的工作
根据您的 IConfiguration 抛出空引用异常的原因,我可以想到 2 种可能性。要么你需要做另一种实例化,就像这样
services.AddSingleton<IConfiguration,Configuration>();
或者可能是因为您没有在 DbContext 本身中使用 DI,所以您不需要执行 new YourContextDbContext()
。你应该只是简单地将它放在服务或控制器的构造函数中,它应该“神奇地”工作,而你实际上不需要创建它的实例。
Also, I am thinking, I might not be instantiating the
SkipQContext
rightly. As my statement:SkipQContext db = new SkipQContext();
hits the default constructor of
SkipQContext
which is empty and not the overloaded constructor whereIConfiguration
is injected.
你是对的,这不是依赖注入的工作方式。每当您执行 new Something
时,您就会 明确地 绕过依赖注入。关于依赖注入的要点是,具有依赖关系的组件(例如数据库上下文)不需要自己创建该依赖关系,甚至 知道 如何自己创建该依赖关系。
当您调用 new SkipQContext()
时,您 是 显式创建该依赖关系,因此您 紧密地 耦合到 SkipQContext
以及上下文正常工作所需的任何内容(在本例中,它需要 DbContextOptions
和 IConfiguration
)。相反,您想要的是组件对其依赖项 loosely coupled 。因此,您只需声明您 需要 的依赖项,并要求某人或其他人为您完成这些依赖项。这正是依赖注入的用武之地:
有了依赖注入,你就有了一个“依赖注入容器”,它负责创建你或某些组件可能需要的所有依赖。您在 Startup.ConfigureServices
的中央位置配置容器,然后您可以通过服务的构造函数简单地声明您需要的依赖项。但是为了让容器向该服务提供这些依赖项,服务必须由由容器本身创建。
所以你将看到的是,你基本上必须通过依赖注入来消耗所有内容。但这也使您在不使用依赖注入时很容易意识到:每当您编写 new Something
时,容器就不会创建某些东西,因此不会自动实现其依赖关系。取决于那东西可能是你想要的,也可能不是(例如,创建一个 List<string>
或一个 DTO 对象是你想直接做的事情,创建一个服务或具有其他依赖关系的东西可能不是).
回到您的问题,为了让 DI 容器处理 SkipQContext
的构造函数中的依赖项,您必须让 DI 容器为您创建该上下文。所以你不能用 new
创建它,而是你必须 depend 将它添加到你需要它的任何组件的构造函数中。
例如如果你有一个控制器,只需将它添加为依赖项:
public class HomeController : Controller
{
private readonly SkipQContext _db;
public HomeController(SkipQContext db)
{
_db = db;
}
public async Task<IActionResult> Index()
{
var items = await _db.Items.ToListAsync();
return View(new IndexViewModel
{
Items = items,
});
}
}
关于您的数据库上下文的最后一点说明:如果您使用 DI 容器 正确地 注册数据库上下文,那么它已经使用获取的 DbContextOptions
配置传递给构造函数。这些选项还将包括上下文打开数据库连接所需的连接字符串。所以你不需要手动传递 IConfiguration
或在那里提取连接字符串。它会由 EF Core 自动为您完成。
正确的上下文设置可能如下所示(在 ConfigureServices
中):
services.AddDbContext<SkipQContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));