2013-04-10 3 views
8

Я пытаюсь написать простой для понимания класс DBContext, который берет пользовательскую строку соединения, может запускать миграции и разрешать мне создавать миграции с помощью диспетчера пакетов.Код первой строки пользовательского подключения и миграции без использования IDbContextFactory

Кажется, я собираюсь кругами.

Я смог заставить его работать с использованием кода, который мне очень нравится. Я документально это в моем ответе на This question on connection string and migrations.

ответ Радека выглядит намного лучше, чем у меня, но я считаю, что, когда я реализую его, а затем попытаться создать миграции в диспетчере пакетов я получаю сообщение

Мишень context 'DataLayer.Context' не является конструктивным. Добавьте конструктор по умолчанию или выполните реализацию IDbContextFactory.

Где DataLayer.Context - мой класс контекста.

Я не хочу, чтобы обеспечить реализацию IDbContextFactory (и ответ Радека, кажется, указывает, что не требуется)

UPDATE:

я могу генерировать миграции, если я включаю конструктор нет параметр. Например

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

Для моего создания контекста я передать имя строки подключения в app.config

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

Наконец я могу и генерировать миграции и запустить миграции баз данных, которые пользователь выбирает.

ОДНАКО: Когда я удаляю базу данных, а затем запускаю свое приложение, у меня проблемы.

Код инициализатора Я использую, по ссылке выше

public class CustomInitializer : IDatabaseInitializer<Context> 
{  
    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<string> 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) не запускается.

Однако, увы, код также не работает во второй раз, когда он должен иметь метаданные.

Теперь, чтобы попытаться получить миграционный ход ...

ПЕЧАЛИ: Нет с Радеком пользовательского инициализатором до сих пор.

Из комментариев 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); 
} 
+0

это выглядит подобный вопрос к этому раствору ответа http://stackoverflow.com/questions/15504465/entityframework-code-first-custom-connection-string-and-migrations/16133150#16133150 –

ответ

7

(примечание: Я не имею ни малейшего представления о пользовательском инициализаторе, я только что видел, что - это общее решение connection caching проблемы и миграции инициализаторе - но должны работать с вариациями)


Это что работает для меня - и на основе ошибочного поведения, которое я описал here.

static string _connection; 
public MyContext() 
    : base(_connection ?? "DefaultConection") 
{ 
    Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, MyNamespace.Migrations.Configuration>()); 
} 
public MyContext(string connection) 
    : base(connection) 
{ 
    _connection = connection; 
    Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, MyNamespace.Migrations.Configuration>()); 
} 

Несколько точек:

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 в то время как приложение работает)

+0

После того, как я внес изменения, приложение стало очень медленным для ru п. –

+0

, который не имеет никакого смысла @kirsteng :) - он ничего не делает в этом смысле - кроме того, что он фактически работает, поэтому он инициализирует и переносит. Повторите попытку - это на самом деле решение всех проблем, поскольку оно охватывает «почему» это происходит: я просто разместил его для вас, так как вижу, что вы запуганы. Это зависит от вас, хотите ли вы это использовать или нет. Приветствия. – NSGaga

+0

И, конечно же, вы должны разобраться в своих особенностях - я не знаю, что делает пользовательский инициализатор, поэтому у вас, вероятно, еще есть работа. Но это тот подход, который вы должны предпринять - и внутри вашего инициализатора. – NSGaga

0

После долгих перетасовки вокруг, я получил эту работу:

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 

Вот класс конфигурации.Обратите внимание на инвалидов 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

Смежные вопросы