加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 创建一个从用户机密获取连接字符串的DbContextFactory

发布时间:2020-12-15 21:06:18 所属栏目:百科 来源:网络整理
导读:使用WebApi项目和包含实体框架实现的单独数据项目处理DotNetCore解决方案.我们一直在升级库,所以我们使用了所有最新的Core. 在Data项目中,我们创建了一个ApplicationDbContextFactory来创建迁移(需要一个无参数构造函数).由于添加迁移时无参数构造函数约束,
使用WebApi项目和包含实体框架实现的单独数据项目处理DotNetCore解决方案.我们一直在升级库,所以我们使用了所有最新的Core.

在Data项目中,我们创建了一个ApplicationDbContextFactory来创建迁移(需要一个无参数构造函数).由于添加迁移时无参数构造函数约束,您无法注入IOptions<>轻松访问appsettings.json值.我们最终使用ConfigurationBuilder来引入WebApi的appsettings.json文件.

我们最近更改了ApplicationDbContextFactory以引入用户机密.这允许每个开发人员使用自定义连接字符串,而不必忽略文件或记住不提交某些内容.

自从进行此更改后,使用dotnet ef迁移添加MIGRATION_NAME在命令行中运行正常.但是,在Visual Studio的程序包管理器控制台中使用添加迁移MIGRATION_NAME现在似乎已被破坏,并显示以下错误:

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中的构造函数.

这是我所指的代码:

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 WebApiappsettings.json
            basePath = basePath.Replace("Data","WebApi");

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

            // Pull in the WebApiappsettings.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”);,但我不知道它们在这个上下文中有任何区别.

我的问题:

>这最初是在RC的RC时代实施的,可能有点过时了.创建迁移时是否有更好的方法来完成提取用户机密值?像这样使用工厂仍然是一件事吗?
>任何人都知道我们为什么在Visual Studio的程序包管理器控制台中收到错误?

解决方法

你发布它已经很久了,但我刚刚发现了这个错误,并找出了原因(尽管没有意义)

问题就在于此

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

如果你在CONNECTION STRING之后将冒号移开它就可以了.我不知道为什么插值中的冒号导致了这个错误

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读