c# - 如何使 EF Core 工具从控制台应用程序的服务提供者处获取 DbContext 实例?

标签 c# entity-framework entity-framework-core

我已阅读 Design-time DbContext Creation 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            
    }
}

问题

如何让 EF Core 工具从控制台应用程序的服务提供者处获取 DbContext 实例?

编辑:

我忘了提到 appsettings.json如下:

{
  "ConnectionStrings": {
    "Sqlite": "Data Source=MyDatabase.db",
    "SqlServer": "Server=(localdb)\\mssqllocaldb;Database=MyDatabase;Trusted_Connection=True"
  }
}

最佳答案

虽然文档主题叫做From application services , 它开始于

If your startup project is an ASP.NET Core app, the tools try to obtain the DbContext object from the application's service provider.

看起来他们不希望 ASP.NET Core 应用程序以外的项目类型使用应用程序服务提供者:)

然后继续

The tools first try to obtain the service provider by invoking Program.BuildWebHost() and accessing the IWebHost.Services property.

example :

public static IWebHost BuildWebHost(string[] args) => ...

这是适用于当前 (EF Core 2.1.3) 位的技巧。这些工具实际上是在包含您的入口点(通常是 Program)的类中搜索名为 BuildWebHoststring[] 的静态(不需要公开)方法args 参数,以及重要的未记录部分 - 返回类型 不需要 IWebHost!它可以是像这样具有公共(public)属性的任何对象

public IServiceProvider Services { get; } 

这给了我们以下解决方案:

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

更新:从 v2.1 开始,您还可以使用新的 CreateWebHostBuilder模式,但恕我直言,它只是增加了这里不需要的另一层复杂性(仍然支持以前的模式)。它是相似的,但现在我们需要一个名为 CreateWebHostBuilder 的方法,它返回一个具有公共(public)方法 Build() 的对象,返回具有公共(public) Services 属性的对象,返回 IServiceProvider。为了在 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;
    }
}

关于c# - 如何使 EF Core 工具从控制台应用程序的服务提供者处获取 DbContext 实例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52511960/

相关文章:

c# - 使用 Entity Framework Core、npgsql、PostgreSQL 创建新数据库

c# - ASP MVC 表单调用特定操作而不是重定向但更新原始页面

c# - Entity Framework 代码首先使用 Where 子句更新

c# - EF.Core查询相关数据

c# - DbContext 必须可转换为 System.Data.Entity.DbContext

c# - 如何等待(在异步方法中)包含 C# 方法调用的 linq 请求

c# - Entity Framework 核心 : Cannot insert explicit value for identity column in table 'Relation' when IDENTITY_INSERT is set to OFF

c# - 递归快速排序遇到 StackOverflowException

c# - NLog在C#中获取消息

c# - NRefactory 缺少 dll