为什么使用自定义DbInitializer的EntityFramework 6.1索引不起作用?

时间:2016-06-30 16:20:03

标签: entity-framework entity-framework-6

我有一个小模特:

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

匹配DbContext

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

当我使用此代码将其放入使用EF6.1的解决方案时:

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

一切都按预期工作。 2. run会因为存在唯一的索引违规而抛出异常。

不幸的是,我需要我的应用程序不要丢弃整个数据库。所以我写了一个表和约束DbInitializer,如下所示:

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

使用此初始化程序,我的索引永远不会访问数据库。其他一切都很好。

ObjectContext.CreateDatabaseScript()不会为索引返回任何SQL:

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

使用默认的DbInitializer,发送给DB的SQL如下所示:

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

然后是另一个这样的声明:

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

有人知道为什么这不起作用吗?

当{_ 1}}返回时,index-SQL不包含在哪里?

1 个答案:

答案 0 :(得分:0)

我不确切知道EF的历史但写了一个EF提供程序(https://jetentityframeworkprovider.codeplex.com/)我发现一些旧的EF版本用于使用DbProviderServices接口生成对象项。该接口(您与初始化程序一起使用的接口)适用于StoreItemCollection,我非常确定它不包含有关索引的信息,但仅包含EntitySet(实体)和{{1} (关系)。在上面的项目中,实现在AssociationSet类。

由于某些EF版本,EF不再使用JetCreateSqlGenerator来生成数据库对象,或者更好的是,如果EF Provider提供DbProviderServices,EF使用该接口,否则EF使用旧版本MigrationSqlGeneratorDbProviderServices提供索引生成和其他迁移实用程序(例如列和表重命名等)。

因此您不会在自定义迁移中看到索引生成,因为您使用的是旧界面。