我想在实体框架代码优先创建数据库时设置数据库的默认排序规则。
我尝试了以下内容:
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");
}
}
当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.
有人可以建议我如何设置校对吗?
答案 0 :(得分:7)
这绝对是可能的,虽然这有点像黑客。您可以使用命令拦截器更改CREATE DATABASE命令。 Il将拦截发送到数据库的所有命令,根据正则表达式识别数据库创建命令,并使用排序规则更改命令文本。
DbInterception.Add(new CreateDatabaseCollationInterceptor("SQL_Romanian_Cp1250_CI_AS_KI_WI"));
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) { }
}
由于数据库是从一开始就使用正确的排序规则创建的,因此所有列都将自动继承该排序规则,之后您不必更改它们。
请注意,它会影响应用程序域内发生的任何后续数据库创建。因此,您可能希望在创建数据库后删除拦截器。
答案 1 :(得分:4)
我能够使用自定义迁移(EF6)更改排序规则。我启用了自动迁移。您需要先删除数据库。
Add-Migration [YourCustomMigration]
来创建迁移代码。 (Code First Migrations)ALTER DATABASE
代码,以便使用所需的数据库归类创建它们。另请注意suppressTransaction
标志:
public override void Up()
{
Sql("ALTER DATABASE [YourDB] COLLATE [YourCollation]", suppressTransaction: true);
[...Your DB Objects Creation codes here...]
}
从那时开始发出的每个update-database
命令都会创建一个新的迁移类。所有迁移代码都按顺序执行。
答案 2 :(得分:2)
我前一段时间遇到过同样的问题。可能的解决方案:
Seed()
方法中更改数据库排序规则
但您可以更改表的各列的排序规则
(注意:没有表格整理这样的东西,它确实与之相关
表中的列)。您必须更改每列的排序规则
分开。Up()
方法中的排序规则。当您使用Seed()
方法时,我会在Seed()
方法中建议以下内容(根据需要进行修改):
context.Database.ExecuteSqlCommand(
@"ALTER TABLE MyTable ALTER COLUMN MyColumn NVARCHAR(max) COLLATE MyCollation NOT NULL");
希望有所帮助。
答案 3 :(得分:2)
我想解释为什么你不应该使用种子方法。如果在添加任何列后更改数据库归类,则collation conflicts
存在较大风险,如下所示
无法解决之间的排序规则冲突 “SQL_Latin1_General_CP1_CI_AS”和“Latin1_General_100_CI_AS”中的 等于操作。
这是因为如果使用ALTER DATABASE [YourDb] COLLATE [YourCollation]
更改数据库,则只会更改数据库排序规则,而不会更改以前创建的列。
T-SQL中的示例:
DECLARE @DBName nvarchar(50), @SQLString nvarchar(200)
SET @DBName = db_name();
SET @SQLString = 'ALTER DATABASE [' + @DBName + '] COLLATE Latin1_General_100_CI_AS'
EXEC(@SQLString)
/* Find Collation of SQL Server Database */
SELECT DATABASEPROPERTYEX(@DBName, 'Collation')
/* Find Collation of SQL Server Database Table Column */
SELECT name, collation_name
FROM sys.columns
WHERE OBJECT_ID IN (SELECT OBJECT_ID
FROM sys.objects
WHERE type = 'U'
AND name = 'AspNetUsers')
AND name = 'FirstName'
因此,您需要在添加任何列之前更改数据库归类或分别更改每个列。可能的解决方案:
我会根据文档中的建议,将DbInterception.Add
放在DbConfiguration
或Application_Start
Global.asax
中的类中。注意:Wherever you put this code, be careful not to execute DbInterception.Add for the same interceptor more than once, or you'll get additional interceptor instances.
public class ApplicationDbConfiguration: DbConfiguration
{
public ApplicationDbConfiguration()
{
DbInterception.Add(new CreateDatabaseCollationInterceptor("Latin1_General_100_CI_AS"));
}
}
我也不会继承接口,而是使用Microsoft在其示例中所做的DbCommandInterceptor
实现。
using System.Data.Common;
using System.Data.Entity.Infrastructure.Interception;
using System.Text.RegularExpressions;
namespace Application.Repositories.EntityFramework
{
public class CreateDatabaseCollationInterceptor : DbCommandInterceptor
{
private readonly string _collation;
public CreateDatabaseCollationInterceptor(string collation)
{
_collation = collation;
}
public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
// Works for SQL Server
if (Regex.IsMatch(command.CommandText, @"^create database \[.*]$"))
{
command.CommandText += " COLLATE " + _collation;
}
}
}
}
@steliosalex:https://stackoverflow.com/a/22895703/3850405。请注意,更改每列可能也不够。您还需要处理存储过程的元数据和参数,并获得数据库在创建这些数据时所具有的排序规则。 完全更改排序规则需要具有正确排序规则的create database命令。
@RahmiAksu https://stackoverflow.com/a/31119371/3850405注意:在我看来,这不是一个好的解决方案,但如果您使用它,请编辑第一次迁移。如果数据库已在生产中,则无法使用。如果您有种子方法,则会抛出异常Resetting the connection results in a different state than the initial login
。
您的Seed SqlException可以通过使用普通的ADO.Net连接来解决,因此不会重置上下文的连接。但是,如上所述,这可能会在以后导致很多错误。
using (var conn = new SqlConnection(context.Database.Connection.ConnectionString))
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText =
string.Format("ALTER DATABASE [{0}] COLLATE Latin1_General_100_CI_AS",
context.Database.Connection.Database));
conn.Open();
cmd.ExecuteNonQuery();
}
}
SqlException:重置连接会导致其他状态不同 比初次登录。登录失败。用户''登录失败。 无法继续执行,因为会话处于kill状态 状态。
来源:
答案 4 :(得分:1)
使用当前版本的EF(EF6)根本不可能。但是,至少EF6 +现在可以使用已存在的数据库。我们已经更改了部署方案,以便我们的部署脚本已经创建了数据库(包括默认排序规则),并让EF6使用现有数据库(使用正确的默认排序规则)。
如果您必须在代码中创建数据库并且不能使用除EF以外的任何其他内容(例如,您无法使用ADO.NET创建数据库),那么您必须选择seliosalex。这是我们提出的唯一解决方案,但是,看到我的评论,做正确的工作还有很多工作。
答案 5 :(得分:1)
我使用 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>();
}
然后通过替换IMigrationsSqlGenerator服务在DbContext中使用它
public class MyDbContext : DbContext
{
//...
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.ReplaceService<IMigrationsSqlGenerator, CustomSqlServerMigrationsSqlGenerator>();
}
//...
}
答案 6 :(得分:0)
EF 5现在支持使用Code First在现有数据库中创建缺失表,因此您可以在运行CF之前创建一个空数据库并设置正确的排序规则。
答案 7 :(得分:0)
1):
public class DataBaseContext : System.Data.Entity.DbContext
{
public DataBaseContext() : base("MyDB") { }
static DataBaseContext()
{
System.Data.Entity.Database.SetInitializer(new MyInitializer());
}
....
}
2):
public class MyInitializer : DropCreateDatabaseIfModelChanges<DataBaseContext>
{
public MyInitializer() { }
protected override void Seed(DataBaseContext context)
{
base.Seed(context);
using (var conn = new SqlConnection(context.Database.Connection.ConnectionString))
{
using (var cmd = conn.CreateCommand())
{
cmd.CommandText =
string.Format("ALTER DATABASE [{0}] COLLATE Persian_100_CI_AS",
context.Database.Connection.Database);
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}
context.Database.Connection.Close();
}
}
}