在不使用IDbContextFactory的情况下编写第一个自定义连接字符串和迁移代码

我正在尝试编写一个易于理解的DBContext类,它采用自定义连接字符串,可以运行迁移,并且我允许我使用包管理器生成迁移。

我好像在四处走动。

我已经能够使用对我来说很糟糕的代码来使用它。 我在关于连接字符串和迁移的这个问题的答案中记录了这一点。

Radek的答案看起来比我的要好得多,但我发现当我实现它然后尝试在Package Manager中创建一个迁移时我收到了消息

目标上下文’DataLayer.Context’不可构造。 添加默认构造函数或提供IDbContextFactory的实现。

DataLayer.Context是我的上下文类。

我不想提供IDbContextFactory的实现(而Radek的回答似乎表明它不需要)

更新:

如果我包含一个没有参数的构造函数,我可以生成一个迁移。 例如

 public Context() : base("ConnectionStringName") { } 

对于我的上下文创建,我在app.config中传递连接字符串的名称

 public Context(string connString) : base(connString) { Database.SetInitializer(new CustomInitializer()); Database.Initialize(true); } 

最后,我能够生成迁移,并为用户选择的数据库运行迁移。

但是:当我删除数据库然后运行我的应用程序时,我遇到了问题。

我正在使用的初始化代码,来自上面的链接

 public class CustomInitializer : IDatabaseInitializer { public void InitializeDatabase(Context context) { try { if (!context.Database.Exists()) { context.Database.Create(); } else { if (!context.Database.CompatibleWithModel(false)) { var configuration = new Configuration(); var migrator = new DbMigrator(configuration); migrator.Configuration.TargetDatabase = new DbConnectionInfo(context.Database.Connection.ConnectionString); IEnumerable migrations = migrator.GetPendingMigrations(); foreach (string migration in migrations) { var scriptor = new MigratorScriptingDecorator(migrator); string script = scriptor.ScriptUpdate(null, migration); context.Database.ExecuteSqlCommand(script); } } } } catch (Exception ex) { } } } 

当我删除数据库时,会创建一个新数据库,但它没有表。 那是因为我的表创建代码都在我的第一次迁移中。

因此!context.Database.CompatibleWithModel(false)条件中的代码不会运行。

然而,唉,代码也不会第二次运行它应该有metadatamodel。

现在尝试让迁移运行…

SADNESS:目前为止没有Radek的自定义初始化程序。

根据NSGaga的评论,如果我改变连接,我已经退出了应用程序。

 var master = new myMDIForm(); master.ConnectionType = connectionType; // being an enum of the different connection names in app.config while (master.ConnectionType != ConnectionType.None ) { Application.Run(master); } 

(注意:我不知道你的自定义初始化程序,我只是看到了 – 这是connection caching问题和迁移初始化程序的一般解决方案 – 但应该使用变体)


这对我有用 – 并且基于我在这里描述的错误行为。

 static string _connection; public MyContext() : base(_connection ?? "DefaultConection") { Database.SetInitializer(new MigrateDatabaseToLatestVersion()); } public MyContext(string connection) : base(connection) { _connection = connection; Database.SetInitializer(new MigrateDatabaseToLatestVersion()); } 

几点:

1)在您的上下文中定义static连接 – 当您设置’新建’时,将其更改为保留latest value 。 这有助于保持事物同步 – 无论谁访问DbContext都有相同的(它在概念上类似于Factory更容易在眼睛上,

2) Migrations是 – MigrateDatabaseToLatestVersion在内部缓存连接(以及内部的整个配置)在一个readonly字段中 – 即使你在DbContext更改它,或者你在外面设置的任何内容,它也会“不同步”

没有办法解决这个问题,而是制作一个“新的初始化程序”。

3)@ Radek发现的实际上是该问题的解决方案 – 并符合(2)。 我刚刚删除了Initialize(true)因为它是不必要的 – 当’时间正确’时会调用它。


就是这样。

有了这个,我现在可以全天候flip我的连接 – 正如我想的那样。
(这意味着我可以在runtime更改连接 – 迁移/创建两个或更多数据库并继续更改连接并同时处理它们)

这是我实际用来在连接之间循环的代码……

 for (var flip = true; true; flip = !flip) { using (var db = new MyContext(flip ? "Name=DefaultConnection" : "Name=OtherConnection")) { // usual db code } } 

关键是set initializer each time you set the connection
旧的仍然存在 – 和旧连接(在应用程序运行时无法删除Db)

经过多次改组后,我得到了这个工作:

 Public Class BlogContext Inherits DbContext Public Sub New() Database.Connection.ConnectionString = Utils.SqlCeConnection.ConnectionString End Sub Private Sub New(Connection As DbConnection) MyBase.New(Connection, True) Database.SetInitializer(New MigrateDatabaseToLatestVersion(Of BlogContext, Migrations.Configuration)) Database.Initialize(True) End Sub Public Shared Function Create() As BlogContext Return New BlogContext(Utils.SqlCeConnection) End Function Public Property Blogs As DbSet(Of Blog) End Class 

这是Configuration类。 请注意禁用的AutomaticMigrations:

 Namespace Migrations Friend NotInheritable Class Configuration Inherits DbMigrationsConfiguration(Of BlogContext) Public Sub New() Me.AutomaticMigrationsEnabled = False End Sub Protected Overrides Sub Seed(Context As BlogContext) End Sub End Class End Namespace 

这是SqlCeConnection助手类,可以在运行时设置数据库位置:

 Public Class Utils Public Shared ReadOnly Property SqlCeConnection As SqlCeConnection Get Return New SqlCeConnection(ConnectionString) End Get End Property Public Shared ReadOnly Property SqlCeConnection(DataSource As String) As SqlCeConnection Get Return New SqlCeConnection(ConnectionString(DataSource)) End Get End Property Private Shared ReadOnly Property ConnectionString As String Get Return ConnectionString(DatabasePath) End Get End Property Private Shared ReadOnly Property ConnectionString(DataSource) As String Get With New SqlCeConnectionStringBuilder .MaxDatabaseSize = 4091 .MaxBufferSize = 1024 .CaseSensitive = False .FlushInterval = 1 .DataSource = DataSource .FileMode = "Read Write" .Encrypt = False .Enlist = True Return .ConnectionString End With End Get End Property Public Shared ReadOnly Property DatabasePath As String Get Return Path.Combine(DatabaseFolder, DatabaseFile) End Get End Property Public Shared ReadOnly Property DatabaseFile As String Get Return "MigrationsDemo.sdf" End Get End Property Public Shared ReadOnly Property DatabaseFolder As String Get DatabaseFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "MigrationsDemo") If Not Directory.Exists(DatabaseFolder) Then Directory.CreateDirectory(DatabaseFolder) End If End Get End Property End Class 

HTH