Как заставить инструменты EF Core получать экземпляр DbContext от поставщика услуг консольного приложения? - PullRequest
0 голосов
/ 26 сентября 2018

Я прочитал Создание во время разработки DbContext , что есть 3 способа, которыми инструменты EF Core (например, команды миграции) получают производный экземпляр DbContext из приложения во время разработки, а не ввремя выполнения.

  • От провайдера службы приложений
  • От любого ctor без параметров
  • От класса, реализующего 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"
  }
}

Ответы [ 3 ]

0 голосов
/ 26 сентября 2018

Хотя тема документации называется Из служб приложений , она начинается с

Если ваш запускаемый проект является приложением 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; } 

, что дает нам следующее решение:

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, но IMHO он просто добавляет еще один уровень сложности, который здесь не нужен (предыдущий шаблон все еще поддерживается).Это похоже, но теперь нам нужен метод с именем CreateWebHostBuilder, который возвращает объект с открытым методом Build(), возвращающий объект с открытым свойством 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;
    }
}
0 голосов
/ 24 марта 2019
namespace gtbweb.Areas.Identity.Pages.Account
{
[AllowAnonymous]
public class RegisterModel : PageModel
{
    private readonly IEmailSender _emailSender;
    private readonly AboutDbContext _theContext;

    public RegisterModel(
        IEmailSender emailSender,
        AboutDbContext theContext)
    {

        _emailSender = emailSender;
        _theContext = theContext;
    }

   public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<CookiePolicyOptions>(options =>
        {

            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });          
                services.AddDbContext<AboutDbContext>(options =>
            options.UseSqlite(
                Configuration.GetConnectionString("DefaultConnection")));
        services.BuildServiceProvider()
        .GetService<Areas.Identity.Pages.Account.RegisterModel>();
    }

Это действительно для ASP.NET 2.2. Зарегистрируйте необходимый вам класс базы данных, используя: services.BuildServiceProvider().GetService<Areas.Identity.Pages.Account.RegisterMode>();

Контекст базы данных будет передан в качестве аргументаконструктор зарегистрированных классов.

0 голосов
/ 26 сентября 2018

Попробуйте, я добавил некоторые изменения, чтобы он заработал:

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.FileExtensions;
using Microsoft.Extensions.Configuration.Json;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.IO;

namespace IOCEFCore
{

    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 IConfigurationRoot _configuration;
        private static readonly string _connectionString;

        static Program()
        {
            _configuration = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).Build();

        }

        static void ConfigureServices(IServiceCollection isc)
        {
            isc.AddSingleton(_ => _configuration);

            isc.AddDbContextPool<TheContext>(options => options.UseInMemoryDatabase("myContext"));

            isc.AddSingleton<TheApp>();
        }

        static void Main()
        {
            IServiceCollection isc = new ServiceCollection();
            ConfigureServices(isc);

            IServiceProvider isp = isc.BuildServiceProvider();

            isp.GetService<TheApp>().Run();
            Console.ReadLine();
        }

        class TheApp
        {
            readonly TheContext _theContext;

            public TheApp(TheContext theContext) => _theContext = theContext;

            public void Run()
            {
                // Do something on _theContext  
                _theContext.Users.Add(new User {Id = 1, Name = "Me"});
                _theContext.SaveChanges();

                foreach (var u in _theContext.Users)
                {
                    Console.WriteLine("{0} : {1}", u.Id, u.Name);
                }
            }
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...