2016-06-30 13 views
3

bekam ich ein kleines Modell:Warum funktionieren meine EntityFramework 6.1-Indizes, die einen benutzerdefinierten DbInitializer verwenden, nicht?

Public Class Context 
    Inherits DbContext 

    Public Sub New() 
    MyBase.New("EfCodeFirstUniqueConstraintTest") 
    End Sub 

    Public Property Things As IDbSet(Of Thing) 

    Protected Overrides Sub OnModelCreating(modelBuilder As DbModelBuilder) 
    MyBase.OnModelCreating(modelBuilder) 
    BuildConstraints(modelBuilder) 
    End Sub 

    Private Sub BuildConstraints(modelBuilder As DbModelBuilder) 
    modelBuilder.Entity(Of Thing).Property(Function(m) m.Name) _ 
     .HasMaxLength(255) _ 
     .HasColumnAnnotation(IndexAnnotation.AnnotationName, _ 
          New IndexAnnotation(New IndexAttribute("UniqueOrgUnitName") _ 
               With {.IsUnique = True})) 
    End Sub 
End Class 

Als ich dies mit EF6.1 in eine Lösung setzen mit diesem Code:

Public Class Thing 
    Public Property Id As Integer 
    Public Property Name As String 
End Class 

A DbContext passende

Sub Main() 
    Using db As New Context 
    Dim t = New Thing With {.Name = "Thingy"} 
    db.Things.Add(t) 
    db.SaveChanges() 
    End Using 
End Sub 

alles funktioniert wie erwartet. Der 2. Lauf wird eine Ausnahme auslösen, da eine eindeutige Indexverletzung vorliegt.

Leider brauche ich meine Anwendung nicht die ganze DB wegwerfen. Also schrieb ich einen Tisch und Zwänge DbInitializer wie diese fallen:

Public Class DbIniter 
    Implements IDatabaseInitializer(Of Context) 
    Public Sub InitializeDatabase(context As Context) Implements IDatabaseInitializer(Of Context).InitializeDatabase 
    DropAllTables(context) 
    Dim dbCreationScript = CType(context, IObjectContextAdapter).ObjectContext.CreateDatabaseScript() 
    context.Database.ExecuteSqlCommand(dbCreationScript) 
    CreateMetaDataTable(context) 
    Seed(context) 
    context.SaveChanges() 
    End Sub 
    Protected Sub DropAllTables(context As Context) 
    context.Database.ExecuteSqlCommand("EXEC sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT all'") 
    Dim remainingTrys = 100 
    Dim everythingOk = False 
    While Not everythingOk AndAlso remainingTrys > 0 
     Try 
     context.Database.ExecuteSqlCommand("EXEC sp_MSforeachtable ""DECLARE @name nvarchar(max); SET @name = parsename('?', 1); EXEC sp_MSdropconstraints @name""") 
     context.Database.ExecuteSqlCommand("EXEC sp_MSforeachtable 'DROP TABLE ?'") 
     everythingOk = True 
     Catch ex As Exception 
     remainingTrys = remainingTrys - 1 
     End Try 
    End While 
    If Not everythingOk Then Throw New System.Data.Entity.Infrastructure.RetryLimitExceededException(String.Format("Database was not empty after last attempt.")) 
    End Sub 

    Protected Sub CreateMetaDataTable(context As Context) 
    Dim sql = "CREATE TABLE dbo.__MigrationHistory (MigrationId NVARCHAR(255) NOT NULL, CreatedOn DATETIME NOT NULL, Model VARBINARY(MAX) NOT NULL, ProductVersion NVARCHAR(32) NOT NULL);" _ 
       & " ALTER TABLE dbo.__MigrationHistory ADD PRIMARY KEY (MigrationId); INSERT INTO dbo.__MigrationHistory (MigrationId, CreatedOn, Model, ProductVersion) VALUES ('InitialCreate', GetDate(), @p0, @p1);" 

    context.Database.ExecuteSqlCommand(sql, GetModel(context), GetProductVersion()) 
    End Sub 

    Protected Function GetModel(context As Context) As Byte() 
    Using memoryStream As New MemoryStream 
     Using gzipStream As New GZipStream(memoryStream, CompressionMode.Compress) 
     Using writer = XmlWriter.Create(gzipStream, New XmlWriterSettings With {.Indent = True}) 
      EdmxWriter.WriteEdmx(context, writer) 
     End Using 
     End Using 
     Return memoryStream.ToArray 
    End Using 
    End Function 

    Protected Overridable Sub Seed(context As Context) 
    End Sub 

    Protected Function GetProductVersion() As String 
    Return GetType(DbContext).Assembly.GetCustomAttributes(False).OfType(Of Reflection.AssemblyInformationalVersionAttribute).Single.InformationalVersion 
    End Function 
End Class 

dieses initializer verwenden, werden meine Indizes schlagen nie die Datenbank. Alles andere funktioniert gut.

ObjectContext.CreateDatabaseScript() wird keine SQL für die Indizes zurück:

create table [dbo].[Things] (
    [Id] [int] not null identity, 
    [Name] [nvarchar](255) null, 
    primary key ([Id]) 
); 

Verwendung des Standard DbInitializer, die SQL an die DB gesendet sieht wie folgt aus:

CREATE TABLE [dbo].[Things] (
    [Id] [int] NOT NULL IDENTITY, 
    [Name] [nvarchar](255), 
    CONSTRAINT [PK_dbo.Things] PRIMARY KEY ([Id]) 
) 

durch eine andere Anweisung wie folgt folgt:

CREATE UNIQUE INDEX [UniqueOrgUnitName] ON [dbo].[Things]([Name]) 

Hat jemand einen Einblick Int o warum das nicht funktioniert?

Woher kommt der Index-SQL, wenn er nicht in dem enthalten ist, was CreateDatabaseScript() zurückgibt?

+0

Die gebündelten EF 'DbInitializer' rufen eine interne 'Database.Create()' - Überladung auf, indem sie einen Parameter vom Typ 'DatabaseExistenceState' verwenden. Wenn ich die public parameterlose Create() 'rufe, bekomme ich eine Ausnahme:" ... kann nicht erstellt werden, da sie bereits existiert ". –

+0

Warum müssen Sie mit MigrationHistory umgehen? Tatsächlich habe ich manchmal ein Problem mit der Validierung (einfach zu sehen, also könnte ich auch mit Indizes haben) und es scheint, dass ich es lösen kann, wenn ich sowohl fließende Schnittstelle als auch Datenannotation verwende, um Entitäten zu konfigurieren (ich weiß, sehr seltsam).Sie könnten versuchen, Datenanmerkungen für Ihre Entität hinzuzufügen. – bubi

+0

Ich erstelle einen MigrationHistory, sodass EntityFramework bemerken wird, dass das Schema aktuell ist. –

Antwort

0

Ich weiß nicht genau die Geschichte von EF, aber einen EF-Provider schreiben (https://jetentityframeworkprovider.codeplex.com/) Ich habe gesehen, dass einige alte EF-Versionen zum Generieren von Objekten mit DbProviderServices Schnittstelle verwendet werden. Diese Schnittstelle (die Schnittstelle, die Sie mit Ihrem Initialisierer verwenden) funktioniert unter StoreItemCollection, die ziemlich sicher ist, dass sie keine Informationen über Indizes enthält, sondern nur über EntitySet (Entitäten) und AssociationSet (Beziehungen). In dem obigen Projekt ist die Implementierung in JetCreateSqlGenerator Klasse.

Seit einiger EF-Version, wird das EF DbProviderServices nicht mehr verwenden, Datenbankobjekte zu erzeugen, oder, besser, wenn der EF-Provider eine MigrationSqlGenerator bietet die EF verwendet diese Schnittstelle die anderweitige Nutzung der EF die alte DbProviderServices. MigrationSqlGenerator bietet Indexgenerierung und andere Migrationsdienstprogramme (z. B. Spalten- und Tabellenumbenennung usw.).

Sie sehen also keine Indexgenerierung in Ihrer benutzerdefinierten Migration, da Sie die alte Schnittstelle verwenden.