Mon*_*mer 4 c# entity-framework entity-framework-core
我已经阅读了设计时 DbContext 创建,EF Core 工具(例如,迁移命令)有 3 种方式DbContext
在设计时而不是在运行时从应用程序获取派生实例。
IDesignTimeDbContextFactory<T>
这里我只对第一种方法感兴趣,它模仿了 Asp.net Core 中使用的模式。这段代码无法编译,因为我不知道如何让 EF Core 工具获取TheContext
实例。
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.IO;
public class TheContext : DbContext
{
public TheContext(DbContextOptions<TheContext> options) : base(options) { }
public DbSet<User> Users { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
private static readonly IConfiguration _configuration;
private static readonly string _connectionString;
static Program()
{
_configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).Build();
_connectionString = _configuration.GetConnectionString("SqlServer");
}
static void ConfigureServices(IServiceCollection isc)
{
isc.AddSingleton(_ => _configuration);
isc.AddDbContextPool<TheContext>(options => options
.UseSqlServer(_connectionString));
isc.AddSingleton<TheApp>();
}
static void Main()
{
IServiceCollection isc = new ServiceCollection();
ConfigureServices(isc);
IServiceProvider isp = isc.BuildServiceProvider();
isp.GetService<TheApp>().Run();
}
}
class TheApp
{
readonly TheContext _theContext;
public TheApp(TheContext theContext) => _theContext = theContext;
public void Run()
{
// Do something on _theContext
}
}
Run Code Online (Sandbox Code Playgroud)
如何让 EF Core 工具从控制台应用程序的服务提供者处获取 DbContext 实例?
我忘了提及appsettings.json
以下内容:
{
"ConnectionStrings": {
"Sqlite": "Data Source=MyDatabase.db",
"SqlServer": "Server=(localdb)\\mssqllocaldb;Database=MyDatabase;Trusted_Connection=True"
}
}
Run Code Online (Sandbox Code Playgroud)
尽管文档主题名为From application services,但它以
如果您的启动项目是 ASP.NET Core 应用程序,这些工具会尝试从应用程序的服务提供程序获取 DbContext 对象。
看起来他们不希望 ASP.NET Core 应用程序以外的项目类型使用应用程序服务提供程序:)
然后它继续
这些工具首先尝试通过调用 Program.BuildWebHost() 并访问 IWebHost.Services 属性来获取服务提供者。
和例子:
public static IWebHost BuildWebHost(string[] args) => ...
这是适用于当前(EF Core 2.1.3)位的技巧。该工具实际上是搜索包含你的入口点(通常是类Program
)的静态(并不需要公开)调用的方法BuildWebHost
与string[] args
参数,以及重要的无证部分-返回类型也并不需要是IWebHost
!它可以是任何具有像这样的公共属性的对象
public IServiceProvider Services { get; }
Run Code Online (Sandbox Code Playgroud)
这为我们提供了以下解决方案:
class Program
{
// ...
// Helper method for both Main and BuildWebHost
static IServiceProvider BuildServiceProvider(string[] args)
{
IServiceCollection isc = new ServiceCollection();
ConfigureServices(isc);
return isc.BuildServiceProvider();
}
static void Main(string[] args)
{
BuildServiceProvider(args).GetService<TheApp>().Run();
}
// This "WebHost" will be used by EF Core design time tools :)
static object BuildWebHost(string[] args) =>
new { Services = BuildServiceProvider(args) };
}
Run Code Online (Sandbox Code Playgroud)
更新:从 v2.1 开始,您还可以使用新CreateWebHostBuilder
模式,但恕我直言,它只是增加了此处不需要的另一层复杂性(仍然支持以前的模式)。它是相似的,但现在我们需要一个被调用的方法CreateWebHostBuilder
,该方法返回一个具有公共方法的对象,该对象具有Build()
返回具有公共Services
属性的对象IServiceProvider
。为了重用 from Main
,我们不能使用匿名类型,必须创建 2 个类,而且这使得它的用法Main
更加冗长:
class AppServiceBuilder
{
public ServiceCollection Services { get; } = new ServiceCollection();
public AppServiceProvider Build() => new AppServiceProvider(Services.BuildServiceProvider());
}
class AppServiceProvider
{
public AppServiceProvider(IServiceProvider services) { Services = services; }
public IServiceProvider Services { get; }
}
class Program
{
// ...
static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Services.GetService<TheApp>().Run();
}
// This "WebHostBuilder" will be used by EF Core design time tools :)
static AppServiceBuilder CreateWebHostBuilder(string[] args)
{
var builder = new AppServiceBuilder();
ConfigureServices(builder.Services);
return builder;
}
}
Run Code Online (Sandbox Code Playgroud)