ASP.NET 核心 EF 模糊构造函数

ASP.NET core EF ambiguous constructor

我使用 ASP.NET Core 和 .NET 5,最近想从本地开发更改为 Azure Web 生产模式。

我在本地使用 SQLite,一切正常,在生产环境中我想使用 Azure SQL。 然而,当我想迁移我的数据库时,我得到一个相当长的异常:

System.Exception: Could not resolve a service of type 'Server.Calendars.CalendarDataContext' for the parameter 'calendarDataContext' of method 'Configure' on type 'Server.Startup'.
 ---> System.InvalidOperationException: Unable to activate type 'Server.Calendars.CalendarDataContext'. The following constructors are ambiguous:
Void .ctor(Microsoft.Extensions.Configuration.IConfiguration)
Void .ctor(Microsoft.EntityFrameworkCore.DbContextOptions`1[Server.Calendars.CalendarDataContext])
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(ResultCache lifetime, Type serviceType, Type implementationType, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, Int32 slot)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(Type serviceType, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateCallSite(Type serviceType, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.<>c__DisplayClass7_0.<GetCallSite>b__0(Type type)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.GetCallSite(Type serviceType, CallSiteChain callSiteChain)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.CreateServiceAccessor(Type serviceType)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
   --- End of inner exception stack trace ---
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.<>c__DisplayClass4_0.<Build>b__0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass15_0.<UseStartup>b__1(IApplicationBuilder app)
   at Microsoft.Extensions.DependencyInjection.AutoRegisterMiddleware.<>c__DisplayClass4_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterBuilderStartupFilter.<>c__DisplayClass0_0.<Configure>g__MiddlewareFilterBuilder|0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.Server.IIS.Core.IISServerSetupFilter.<>c__DisplayClass2_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.HostFilteringStartupFilter.<>c__DisplayClass0_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken)

我的 class CalendarDataContext .cs Azure SQL

public class CalendarDataContext : DbContext
{
    public DbSet<CalendarEntry> CalendarEntries { get; set; }


    protected readonly IConfiguration Configuration;

    public CalendarDataContext(IConfiguration configuration)
    {
        Configuration = configuration;
    }

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

    protected override void OnConfiguring(DbContextOptionsBuilder options)
    {
        if (!options.IsConfigured)
        {
            options.UseSqlServer(Configuration.GetConnectionString("CalendarDatabase"));
        }
    }
}

CalendarDataContextSqlite.cs 用于 SQLite

public class CalendarDataContextSqlite : CalendarDataContext
{
    public CalendarDataContextSqlite(IConfiguration configuration) : base(configuration) { }

    protected override void OnConfiguring(DbContextOptionsBuilder options)
    {
        if (!options.IsConfigured)
        {
            var databaseName = Configuration.GetConnectionString("CalendarDatabase");
            var databasePath = PathHelper.DataPath(databaseName);
            options.UseSqlite("Data Source=" + databasePath);
        }
    }
}

我认为问题出在行 CalendarDataContext(DbContextOptions<CalendarDataContext> options) 上,我需要它来为我的测试创建临时 InMemory 数据库。

如何使这个不明确的构造函数不那么不明确?

编辑:添加startup.cs

public class Startup
{
    public IWebHostEnvironment Environment { get; }

    public Startup(IConfiguration configuration, IWebHostEnvironment environment)
    {
        Environment = environment;
    }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        if (Environment.IsProduction())
        {
            services.AddDbContext<CalendarDataContext>();
        }
        else if (Environment.IsDevelopment())
        {
            services.AddDbContext<CalendarDataContext, CalendarDataContextSqlite>();
        }
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app,
        IWebHostEnvironment env,
        CalendarDataContext calendarDataContext)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        calendarDataContext.Database.Migrate();
    }
}

首先,将 IConfiguration 添加为本地成员 Startup.cs

IConfiguration Configuration;
public IWebHostEnvironment Environment { get; }

public Startup(IConfiguration configuration, IWebHostEnvironment environment)
{
    Configuration = configuration;
    Environment = environment;
}

然后在 Startup

中注册一个配置的 CalendarDataContext 或 CalendarDataContextSqlite
public void ConfigureServices(IServiceCollection services)
{
    if (Environment.IsProduction())
    {
        services.AddDbContext<CalendarDataContext>(options => 
            options.UseSqlServer(
                Configuration.GetConnectionString("CalendarDatabase"));
    }
    else if (Environment.IsDevelopment())
    {
        services.AddDbContext<CalendarDataContext, CalendarDataContextSqlite>(options => {
            var databaseName = Configuration.GetConnectionString("CalendarDatabase");
            var databasePath = PathHelper.DataPath(databaseName);
            options.UseSqlite("Data Source=" + databasePath);
        });
    }
}

然后,CalendarDataContext:

public class CalendarDataContext : DbContext
{
    public DbSet<CalendarEntry> CalendarEntries { get; set; }


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

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

并且,CalendarDataContextSqlite:

public class CalendarDataContextSqlite : CalendarDataContext
{
    public CalendarDataContextSqlite(DbContextOptions<CalendarDataContextSqlite> options)
        : base(options) { }
}

现在,

上下文中不需要 OnConfiguring 类。

在生产中,您将有一个配置好的 CalendarDataContext 被注入到构造函数要求 CalendarDataContext 的任何地方。

对于开发,您将拥有一个已配置的 CalendarDataContextSqlite,可以在构造函数要求 CalendarDataContext 的任何地方注入。

配置的上下文也将被注入 Startup.Configure 以便您可以迁移数据库。