【问题标题】:EF Core migrations on a new SQL Server新 SQL Server 上的 EF Core 迁移
【发布时间】:2020-05-29 11:59:03
【问题描述】:

我正在尝试构建一个能够从头开始的迁移控制台应用程序,即在新安装的 SQL Server 上,包括具有凭据的专用用户,该用户应该是 db_owner。

我在 PostgreSQL 上运行它,但需要为 SQL Server 获得类似的解决方案。

在我打电话给context.Database.Migrate() 之前,我打电话给CheckDatabaseCreated(context.Database, configuration),这基本上是这样做的:

  1. 尝试使用给定的连接字符串进行连接

  2. 如果失败,它将用 SA 和 SA 密码替换用户和密码 并连接到master。

  3. 如果登录不存在,则创建登录。

  4. 如果数据库不存在,则创建它。

  5. 连接到新创建的数据库 - 仍然是 SA。

  6. 使用登录创建用户,并添加 db_owner 角色。

  7. 最后它再次尝试使用原始连接字符串进行连接 - 这就是失败的地方。

使用 HeidiSQL,我可以看到数据库已创建,但我只能使用 SA 凭据进行连接。

    private static void CheckDatabaseCreated(DatabaseFacade contextDatabase, IConfiguration configuration)
    {
        bool canConnect;
        try
        {
            canConnect = contextDatabase.CanConnect();
            Console.WriteLine("Database connected succesfully.");
        }
        catch (Exception e)
        {
            Console.WriteLine($"Unable to connect to database: {e.Message}");
            canConnect = false;
        }
        if (!canConnect)
        {
            var builder = new SqlConnectionStringBuilder(configuration["ConnectionString"]);
            var originalUser = builder.UserID;
            var originalPassword = builder.Password;
            var originalDatabase = builder.InitialCatalog;
            builder.UserID = _masterUsername;
            builder.Password = _masterPassword;
            builder.InitialCatalog = "master";
            var login = $"{originalUser}Login";

            SqlConnection conn = new SqlConnection(builder.ConnectionString);
            try
            {
                conn.Open();
                // Check if login exists
                SqlCommand command = new SqlCommand($"SELECT COUNT(*) FROM master.sys.server_principals WHERE name = '{login}'", conn);
                object result = command.ExecuteScalar();
                result = (result == DBNull.Value) ? null : result;
                if (Convert.ToInt32(result) < 1)
                {
                    Console.WriteLine("Login does not exist - creating.");
                    command = new SqlCommand($"CREATE LOGIN [{login}] WITH PASSWORD = N'{originalPassword}', CHECK_POLICY = OFF, CHECK_EXPIRATION = OFF", conn);
                    command.ExecuteNonQuery();
                }
                // Check if database exists
                command = new SqlCommand($"SELECT COUNT(*) FROM master.sys.databases WHERE name = '{originalDatabase}'", conn);
                result = command.ExecuteScalar();
                result = (result == DBNull.Value) ? null : result;
                if (Convert.ToInt32(result) < 1)
                {
                    Console.WriteLine("Database does not exist - creating.");
                    command = new SqlCommand($"CREATE DATABASE \"{originalDatabase}\" ", conn);
                    command.ExecuteNonQuery();
                }
                conn.Close();
                // Now connect to the (newly created) database - still as sa.
                builder.InitialCatalog = originalDatabase;
                conn = new SqlConnection(builder.ConnectionString);
                try
                {
                    conn.Open();
                    command = new SqlCommand($"CREATE USER [{originalUser}] FOR LOGIN [{login}]", conn);
                    command.ExecuteNonQuery();

                    command = new SqlCommand($"EXEC sp_addrolemember 'db_owner', '{originalUser}'", conn);
                    command.ExecuteNonQuery();

                    conn.Close();
                }
                catch (Exception e)
                {
                    Console.WriteLine($"Unable to connect to {originalDatabase} database: {e.Message}");
                }
                // Finally try to connect as the user created above.
                builder = new SqlConnectionStringBuilder(configuration["ConnectionString"]);
                conn = new SqlConnection(builder.ConnectionString);
                try
                {
                    conn.Open();
                }
                catch (Exception e)
                {
                    // This is where it fails.
                    Console.WriteLine($"Unable to connect to database: {e.Message}");
                }
            }
            catch (Exception e)
            {
                Console.WriteLine($"Unable to connect to database: {e.Message}");
            }
        }
    }

【问题讨论】:

    标签: sql-server ef-core-2.2 dbmigrate


    【解决方案1】:

    SQL Server 连接字符串中的User ID 指的是登录名或包含的数据库用户。

    所以你的问题就在这里:

       var login = $"{originalUser}Login";
    

    此登录不是您的连接字符串中引用的登录。没有理由登录和数据库用户需要不同的名称。所以只要让它们一样:

       var login = originalUser;
    

    【讨论】:

    • 非常感谢,我发现它与单独的登录名和用户名相混淆。
    • 根据您建议的更正,它可以工作 - 在单步执行代码时。我发现在添加用户角色之后,在以该用户身份连接之前,我需要一个 Thread.Sleep(5000) 。知道为什么吗? (3000 毫秒是不够的)
    • 这不应该发生,并且用户连接不需要角色成员身份。而且我无法重现这种行为。
    • 我可能在这里过河取水。许多帖子声称 context.Database.Migrate() 将创建数据库,如果它不存在的话。如果需要创建数据库,我无法找到有关如何提供 SA 凭据的任何说明 - 我当然不希望应用程序以系统管理员权限运行。
    • 是的。但这里更常见的方法可能是使用脚本部署数据库,而不是在运行时应用迁移。例如docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/…
    猜你喜欢
    • 2021-01-16
    • 2021-11-23
    • 1970-01-01
    • 2019-04-11
    • 2022-11-11
    • 2017-05-27
    • 2018-07-18
    • 1970-01-01
    • 2021-03-21
    相关资源
    最近更新 更多