创建一个从用户秘密获取连接字符串的DbContextFactory [英] Creating a DbContextFactory that gets the connection string from user-secrets

查看:83
本文介绍了创建一个从用户秘密获取连接字符串的DbContextFactory的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用一个 WebApi 项目和一个单独的 Data 项目(包含实体框架实现)来开发DotNetCore解决方案。我们一直在升级库,因为它们出来了,所以我们使用了所有最新的Core东西。

Working on a DotNetCore solution with a WebApi project and a separate Data project that houses the Entity Framework implementation. We've been upgrading libraries as they come out, so we're using all of the newest Core stuff.

Data 项目,我们创建了一个 ApplicationDbContextFactory 以便创建迁移(需要无参数构造函数)。由于添加迁移时无参数构造函数的限制,您不能注入 IOptions<> 来轻松访问 appsettings.json 值。我们最终使用了 ConfigurationBuilder 插入 WebApi appsettings.json 文件。

In the Data project, we created an ApplicationDbContextFactory in order to create migrations (needs a parameterless constructor). Due to the parameterless constructor constraint when adding a migration, you can't inject IOptions<> to easily access appsettings.json values. We ended up using a ConfigurationBuilder to pull in the WebApi's appsettings.json file(s).

我们最近更改了 ApplicationDbContextFactory 来加入用户秘密。这样,每个开发人员都可以使用自定义的连接字符串,而不必忽略文件或记住不要提交任何内容。

We recently changed the ApplicationDbContextFactory to also pull in user-secrets. This allows each developer to use a custom connection string without having to ignore a file or remember to not commit something.

由于进行了此更改,因此使用 dotnet ef迁移添加MIGRATION_NAME 在命令行中效果很好。但是,现在在Visual Studio的Package Manager控制台中使用 add-migration MIGRATION_NAME 似乎已被破坏,并出现以下错误:

Since making this change, using dotnet ef migrations add MIGRATION_NAME works just fine in command line. However, using add-migration MIGRATION_NAME in Visual Studio's Package Manager Console now appears to be broken with the following error:


add-migration:使用 1作为参数调用 Substring的异常:
StartIndex不能小于零。参数名称:startIndex在
行: 1个字符:1 + add-migration测试+ ~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo:未指定:(:) [Add-Migration], MethodInvocationException
+ FullyQualifiedErrorId:ArgumentOutOfRangeException,Add-Migration

add-migration : Exception calling "Substring" with "1" argument(s): "StartIndex cannot be less than zero. Parameter name: startIndex" At line:1 char:1 + add-migration TESTING + ~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [Add-Migration], MethodInvocationException + FullyQualifiedErrorId : ArgumentOutOfRangeException,Add-Migration

我尝试了一些命令变体,看是否需要要指定上下文(除其他事项外),但似乎没有任何解决此错误的方法。它似乎永远不会绕过 ApplicationDbContextFactory 中的构造函数。

I tried a few variations of the command to see if it needed the context to be specified (among other things), but nothing seemed to get around this error. It never seems to get past the constructor in ApplicationDbContextFactory.

这是我指的代码:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Models.Domain.Settings;
using System;
using System.Diagnostics;

namespace Data
{
    public class ApplicationDbContextFactory : IDbContextFactory<ApplicationDbContext>
    {
        private readonly SolutionSettings _settings;

        // In order to use 'add-migration' in Visual Studio, you have to have a parameterless constructor.
        // Otherwise you get "No parameterless constructor defined for this object." when creating a migration.
        public ApplicationDbContextFactory()
        {
        }

        public ApplicationDbContextFactory(IOptions<SolutionSettings> settings)
        {
            _settings = settings.Value;
        }

        public ApplicationDbContext Create(DbContextFactoryOptions options)
        {
            // If the IOptions signature was hit, we can just pull the dbconnection from settings
            if (_settings != null && _settings.DbConnection != null)
            {
                var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>()
                    .UseSqlServer(_settings.DbConnection, opts => {
                        opts.EnableRetryOnFailure();
                        opts.MigrationsAssembly("Data");
                    });

                return new ApplicationDbContext(optionsBuilder.Options);
            }
            else
            {
                // Otherwise, we have to get the settings manually...
                return Create(options.ContentRootPath, options.EnvironmentName);
            }
        }

        private ApplicationDbContext Create(string basePath, string environmentName)
        {
            // HACK: To pull from WebApi\appsettings.json
            basePath = basePath.Replace("Data", "WebApi");

            Console.Write($"PATH & ENV: {basePath}, {environmentName}" + Environment.NewLine);

            // Pull in the WebApi\appsettings.json files, apply user secrets
            var builder = new ConfigurationBuilder()
                .SetBasePath(basePath)
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .AddJsonFile($"appsettings.{environmentName.ToLower()}.json", optional: true, reloadOnChange: true)
                // This needs to match the UserSecretsId value in the WebApi.csproj
                // Also added a UserSecretsId key with the same value to Data.csproj to suppress a warning
                // Adding this was the only way it would actually override values with user-secret values
                .AddUserSecrets("USER_SECRETS_ID")
                .AddEnvironmentVariables();

            var config = builder.Build();
            var connectionString = config["SolutionSettings:DbConnection"];

            Console.Write($"CONNECTION STRING: {connectionString}" + Environment.NewLine);

            return Create(connectionString);
        }

        private ApplicationDbContext Create(string connectionString)
        {
            if (string.IsNullOrEmpty(connectionString))
                throw new ArgumentException(
                    $"{nameof(connectionString)} is null or empty.",
                    nameof(connectionString));

            var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>()
                .UseSqlServer(connectionString, options => {
                    options.EnableRetryOnFailure();
                    options.MigrationsAssembly("Data");
                });

            return new ApplicationDbContext(optionsBuilder.Options);
        }
    }
}

在解决此问题时,我添加了 opts.EnableRetryOnFailure(); opts.MigrationsAssembly( Data); ,但是我不知道它们在这种情况下没有任何作用。

As a side note; while troubleshooting this I added opts.EnableRetryOnFailure(); and opts.MigrationsAssembly("Data");, but I don't know that they make any difference in this context.

我的问题:


  • 这最初是在Core的RC时代实施的,可能有些过时了。创建迁移时,是否有更好的方法来完成对用户秘密值的提取?

  • 有人知道为什么我们会在Visual Studio的Package Manager控制台中收到该错误吗?

推荐答案

自您发布以来已经很长时间了,但是我只是遇到了这个错误并找出了原因(即使它使

It's been a long time since you posted it, but I've just had this error and found out the reason (even though it makes no sense)

问题出在

console.Write($"CONNECTION STRING: {connectionString}" + Environment.NewLine);

如果在连接字符串后移动冒号,它将起作用。
我不知道为什么插值中的冒号会导致此错误

If you romove the colon after CONNECTION STRING it works. I have no idea why the colon in the interpolation is causing this error

这篇关于创建一个从用户秘密获取连接字符串的DbContextFactory的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆