在Entity Framework Code-First Initializer中设置数据库排序规则

App*_*ere 18 sql-server ef-code-first entity-framework-4.1

我想在Entity Framework Code First创建数据库时设置数据库的默认排序规则.

我尝试过以下方法:

public class TestInitializer<T> : DropCreateDatabaseAlways<T> where T: DbContext
{
    protected override void Seed(T context)
    {
        context.Database.ExecuteSqlCommand("ALTER DATABASE [Test] SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
        context.Database.ExecuteSqlCommand("ALTER DATABASE [Test] COLLATE Latin1_General_CI_AS");
        context.Database.ExecuteSqlCommand("ALTER DATABASE [Test] SET MULTI_USER");
    }
}
Run Code Online (Sandbox Code Playgroud)

当SQL Server 设置为相同的默认排序规则Latin1_General_CI_AS 时,这似乎运行正常.

但是如果我指定了不同的排序规则,比如说SQL_Latin1_General_CP1_CI_AS 则会因错误而失败,

System.Data.SqlClient.SqlException: Resetting the connection results in a different 
state than the initial login. The login fails.
Run Code Online (Sandbox Code Playgroud)

任何人都可以建议我如何设置校对吗?

Mat*_*nda 8

使用命令拦截器的解决方案

这绝对是可能的,虽然它有点像黑客.您可以使用命令拦截器更改CREATE DATABASE命令.Il将拦截发送到数据库的所有命令,根据正则表达式识别数据库创建命令,并使用排序规则更改命令文本.

在创建数据库之前

DbInterception.Add(new CreateDatabaseCollationInterceptor("SQL_Romanian_Cp1250_CI_AS_KI_WI"));
Run Code Online (Sandbox Code Playgroud)

拦截器

public class CreateDatabaseCollationInterceptor : IDbCommandInterceptor
{
    private readonly string _collation;

    public CreateDatabaseCollationInterceptor(string collation)
    {
        _collation = collation;
    }

    public void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext) { }
    public void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
    {
        // Works for SQL Server
        if (Regex.IsMatch(command.CommandText, @"^create database \[.*]$"))
        {
            command.CommandText += " COLLATE " + _collation;
        }
    }
    public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) { }
    public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) { }
    public void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) { }
    public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) { }
}
Run Code Online (Sandbox Code Playgroud)

备注

由于数据库是从一开始就使用正确的排序规则创建的,因此所有列都将自动继承该排序规则,之后您不必更改它们.

请注意,它将影响在应用程序域内发生的任何后续数据库创建.因此,您可能希望在创建数据库后删除拦截器.

  • 我将其放入“ Configuration”类的构造方法中(“ Seed”方法所在的地方),它工作得很好。该答案应标记为正确。 (2认同)

Rah*_*ksu 7

我能够通过自定义迁移 (EF6) 更改排序规则。我启用了自动迁移。您需要先删除您的数据库。

  1. 通过Add-Migration [YourCustomMigration]在包管理器控制台中键入来创建迁移代码。(代码优先迁移
  2. 第一步应该使用 Up() 覆盖中的当前模型创建代码创建迁移类。ALTER DATABASE在表创建代码之前添加您的代码,以便使用您想要的数据库排序规则创建它们。另外,请注意suppressTransaction标志:

public override void Up() { Sql("ALTER DATABASE [YourDB] COLLATE [YourCollation]", suppressTransaction: true); [...Your DB Objects Creation codes here...] }

update-database从那时起发出的每个命令都会创建一个新的迁移类。所有迁移代码都按顺序执行。

  • 我收到这样的错误:重置连接导致与初始登录不同的状态。登录失败。用户 'debug' 登录失败。无法继续执行,因为会话处于终止状态。当前命令发生严重错误。结果,如果有的话,应该被丢弃。 (3认同)

Cat*_* M. 7

我使用EFCore 的解决方案是从 SqlServerMigrationsSqlGenerator 派生并覆盖 Generate(SqlServerCreateDatabaseOperation, IModel, MigrationCommandListBuilder)

    internal class CustomSqlServerMigrationsSqlGenerator : SqlServerMigrationsSqlGenerator
    {
        internal const string DatabaseCollationName = "SQL_Latin1_General_CP1_CI_AI";

        public CustomSqlServerMigrationsSqlGenerator(
            MigrationsSqlGeneratorDependencies dependencies,
            IMigrationsAnnotationProvider migrationsAnnotations)
        : base(dependencies, migrationsAnnotations)
        {
        }

        protected override void Generate(
            SqlServerCreateDatabaseOperation operation,
            IModel model,
            MigrationCommandListBuilder builder)
        {
            base.Generate(operation, model, builder);

            if (DatabaseCollationName != null)
            {
                builder
                    .Append("ALTER DATABASE ")
                    .Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name))
                    .Append(" COLLATE ")
                    .Append(DatabaseCollationName)
                    .AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator)
                    .EndCommand(suppressTransaction: true);
            }
        }
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        base.OnConfiguring(optionsBuilder);
        optionsBuilder.ReplaceService<IMigrationsSqlGenerator, CustomSqlServerMigrationsSqlGenerator>();
    }
Run Code Online (Sandbox Code Playgroud)

然后通过替换 IMigrationsSqlGenerator 服务在 DbContext 中使用它

public class MyDbContext : DbContext
{
    //...

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        base.OnConfiguring(optionsBuilder);
        optionsBuilder.ReplaceService<IMigrationsSqlGenerator, CustomSqlServerMigrationsSqlGenerator>();
    }

    //...
}
Run Code Online (Sandbox Code Playgroud)


小智 5

不久前我遇到了同样的问题。可能的解决方案:

  1. EF 似乎使用服务器默认排序规则创建数据库,因此您可以做的一件事就是更改它。
  2. 您不能在该Seed()方法中更改数据库排序规则,但可以更改表的各个列的排序规则(注意:没有诸如表排序规则之类的东西,它确实与表中的列有关)。您必须分别更改每列的排序规则。
  3. 如果您正在使用迁移,您可以在您的Up()方法中更改表列排序规则。

当您使用该Seed()方法时,我建议在该方法中进行以下操作(酌情修改)Seed()

context.Database.ExecuteSqlCommand(
@"ALTER TABLE MyTable ALTER COLUMN MyColumn NVARCHAR(max) COLLATE MyCollation NOT NULL");
Run Code Online (Sandbox Code Playgroud)

希望有帮助。


Pet*_*ter 0

EF 5 现在支持使用 Code First 在现有数据库中创建缺失的表,因此您可以在运行 CF 之前创建一个空数据库并正确设置排序规则。