ASP.NET 核心 EF 歧义构造函数

问题描述

我将 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,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)

我的 Azure SQL 课程 CalendarDataContext .cs

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

我认为问题在于我需要为测试创建临时 InMemory-Database 的行 CalendarDataContext(DbContextOptions<CalendarDataContext> options)

我怎样才能让这个模糊的构造函数不那么模糊?

编辑:添加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();
    }
}

解决方法

首先,在 Startup.cs 中添加 IConfiguration 作为本地成员

IConfiguration Configuration;
public IWebHostEnvironment Environment { get; }

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

然后在启动时注册一个配置的 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 中,以便您可以迁移数据库。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...