Mar*_*921 25 c# ef-code-first sql-server-ce-4 ef-migrations
我不确定如何使用代码首次迁移功能.根据我的理解,它应该创建我的数据库(如果它已经不存在),并根据迁移文件将其更新为最新的模式.但我正在努力解决它,因为我总是会遇到很多错误,而且我不确定如何正确使用它.
internal class Program
{
private static void Main()
{
EntityFrameworkProfiler.Initialize();
Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Migrations.Configuration>());
using (var context = new MyContext())
{
var exists = context.Database.Exists();
if (!exists)
{
context.Database.Create();
}
var element = context.Dummies.FirstOrDefault();
}
}
}
public class MyContext : DbContext
{
public MyContext()
: base(string.Format(@"DataSource=""{0}""", @"C:\Users\user\Desktop\MyContext.sdf"))
{
}
public DbSet<Dummy> Dummies { get; set; }
}
internal sealed class Configuration : DbMigrationsConfiguration<MyContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
}
protected override void Seed(CodeFirstTest.MyContext context)
{
}
}
Run Code Online (Sandbox Code Playgroud)
使用Entity Framework Profiler我检查执行的语句.当我运行没有数据库的程序时,我得到以下输出:
- 语句#1 SELECT [GroupBy1].[A1] AS [C1] FROM(SELECT COUNT(1)AS [A1] FROM [__ MigrationHistory] AS [Extent1])AS [GroupBy1]
- 语句#2 WARN:System.Data.SqlServerCe.SqlCeException(0x80004005):指定的表不存在.位于System.Data.SqlServerCe.SqlCeCommand.CompileQueryPlan()的System.Data.SqlServerCe.SqlCeCommand.ProcessResults(Int32hr)的System.Data.SqlServerCe.SqlCeCommand.ExecuteCommand(CommandBehavior behavior,String method,ResultSetOptions options)中的[__ Migration Migration]位于System.Data.SqlServerCe.SqlCeMultiCommand.ExecuteReader(CommandBehavior行为)的System.Data.SqlServerCe.SqlCeCommand.ExecuteReader(CommandBehavior behavior),位于HibernatingRhinos.Profiler.Appender.ProfiledDataAccess的System.Data.SqlServerCe.SqlCeMultiCommand.ExecuteDbDataReader(CommandBehavior行为). ProfiledCommand.ExecuteDbDataReader(CommandBehavior行为)
- 语句#3 SELECT [GroupBy1].[A1] AS [C1] FROM(SELECT COUNT(1)AS [A1] FROM [__ MigrationHistory] AS [Extent1])AS [GroupBy1]
- 语句#4 WARN:System.Data.SqlServerCe.SqlCeException(0x80004005):指定的表不存在.位于System.Data.SqlServerCe.SqlCeCommand.CompileQueryPlan()的System.Data.SqlServerCe.SqlCeCommand.ProcessResults(Int32hr)的System.Data.SqlServerCe.SqlCeCommand.ExecuteCommand(CommandBehavior behavior,String method,ResultSetOptions options)中的[__ Migration Migration]位于System.Data.SqlServerCe.SqlCeMultiCommand.ExecuteReader(CommandBehavior行为)的System.Data.SqlServerCe.SqlCeCommand.ExecuteReader(CommandBehavior behavior),位于HibernatingRhinos.Profiler.Appender.ProfiledDataAccess的System.Data.SqlServerCe.SqlCeMultiCommand.ExecuteDbDataReader(CommandBehavior行为). ProfiledCommand.ExecuteDbDataReader(CommandBehavior行为)
- 语句#5 SELECT [GroupBy1].[A1] AS [C1] FROM(SELECT COUNT(1)AS [A1] FROM [__ MigrationHistory] AS [Extent1])AS [GroupBy1]
- 语句#6 WARN:System.Data.SqlServerCe.SqlCeException(0x80004005):指定的表不存在.位于System.Data.SqlServerCe.SqlCeCommand.CompileQueryPlan()的System.Data.SqlServerCe.SqlCeCommand.ProcessResults(Int32hr)的System.Data.SqlServerCe.SqlCeCommand.ExecuteCommand(CommandBehavior behavior,String method,ResultSetOptions options)中的[__ Migration Migration]位于System.Data.SqlServerCe.SqlCeMultiCommand.ExecuteReader(CommandBehavior行为)的System.Data.SqlServerCe.SqlCeCommand.ExecuteReader(CommandBehavior behavior),位于HibernatingRhinos.Profiler.Appender.ProfiledDataAccess的System.Data.SqlServerCe.SqlCeMultiCommand.ExecuteDbDataReader(CommandBehavior行为). ProfiledCommand.ExecuteDbDataReader(CommandBehavior行为)
- 语句#7 SELECT [GroupBy1].[A1] AS [C1] FROM(SELECT COUNT(1)AS [A1] FROM [__ MigrationHistory] AS [Extent1])AS [GroupBy1]
- 语句#8 WARN:System.Data.SqlServerCe.SqlCeException(0x80004005):指定的表不存在.位于System.Data.SqlServerCe.SqlCeCommand.CompileQueryPlan()的System.Data.SqlServerCe.SqlCeCommand.ProcessResults(Int32hr)的System.Data.SqlServerCe.SqlCeCommand.ExecuteCommand(CommandBehavior behavior,String method,ResultSetOptions options)中的[__ Migration Migration]位于System.Data.SqlServerCe.SqlCeMultiCommand.ExecuteReader(CommandBehavior行为)的System.Data.SqlServerCe.SqlCeCommand.ExecuteReader(CommandBehavior behavior),位于HibernatingRhinos.Profiler.Appender.ProfiledDataAccess的System.Data.SqlServerCe.SqlCeMultiCommand.ExecuteDbDataReader(CommandBehavior行为). ProfiledCommand.ExecuteDbDataReader(CommandBehavior行为)
- 语句#9以隔离级别开始事务:可序列化
- 语句#10 CREATE TABLE [Dummies]([Name] nvarchar NOT NULL,CONSTRAINT [PK_Dummies] PRIMARY KEY([Name]))
- 语句#11 CREATE TABLE [ MigrationHistory]([MigrationId] nvarchar NOT NULL,[CreatedOn] [datetime] NOT NULL,[Model] [image] NOT NULL,[ProductVersion] nvarchar NOT NULL,CONSTRAINT [PK _MigrationHistory] PRIMARY KEY ([MigrationId]))
- 声明#12 INSERT INTO [__ MigigrationHistory]([MigrationId],[CreatedOn],[Model],[ProductVersion])VALUES('201207261524579_InitialCreate','2012-07-26T15:24:58.523',0x1F8B080,'4.3. 1' )
- 声明#13提交事务
- 声明#14 SELECT TOP(1)[c].[Name] AS [Name] FROM [Dummies] AS [c]
正如您所看到的,它在实际创建数据库之前尝试访问数据库四次.这似乎不对.当我使用现有数据库启动应用程序时,它将在执行任何实际查询之前查询数据库7次.请注意,这发生在context.Database.Create(),而不是.Exists().
我的配置的种子方法永远不会被调用,但构造函数是.
这一切似乎都非常错误和令人困惑.我希望有人可以告诉我为什么错误在开始时经常发生,以及为什么我的种子方法根本不被调用.
我正在使用SqlServer compact和Entity Framework的最新稳定版本.
package id ="EntityFramework"version ="4.3.1"targetFramework ="net40"
package id ="Microsoft.SqlServer.Compact"version ="4.0.8854.2"targetFramework ="net40"
Ben*_*yne 12
似乎有很多方法可以配置实体框架,每个人都有自己的最佳选择.我所能提供的只是基于我们在工作中标准化的内容.很多这是开发者的偏好.我的偏好恰好是尽可能地控制,所以我总是能够确切地了解发生了什么以及何时发生.
首先,虽然自动迁移可能很方便但是它们会带来很多麻烦,特别是当项目增长和/或数据变得更加复杂时.在我看来,任何商业/生产系统都应该有更多的控制权.我们总是通过设置来关闭所有主要项目的自动迁移AutomaticMigrationsEnabled = false;.当我们想要完成迁移时,我们会明确地运行我们的迁移(在开发中,这是在visual studio中的包管理器控制台中通过键入Update-Database和生产我们已经编写了我们自己的小迁移实用程序,它只是调用迁移到最新的代码 - 但没有一个是自动的).
@ Terric的答案让我感到害怕,因为自动迁移和数据丢失都是允许的!我不想成为部署解决方案并清除一些重要数据的人,因为执行错误的列更改会导致数据丢失.作为旁注,当我们在dev中明确地运行迁移时,我经常使用-v开关来表示详细的ouptut(Update-Database -v).这使您可以查看正在执行的SQL以及任何适当的失败/警告.
我们的经验是,在您进行多次迁移后,更改这些设置并不顺利.我不确定跟踪它的位置,但启动一个禁用自动迁移的新项目可确保不会发生任何意外情况.
就个人而言,我会删除你拥有的初始化程序,MigrateDatabaseToLatestVersion并在我想要的时候自己运行迁移器(通过包管理器控制台或通过我自己的显式代码).
此行为由DatabaseInitializer(实际上不是EntityFramework本身)提供.在CreateDatabaseIfNotExists初始化内置的EntityFramework,并在一些版本的缺省值.但是,我并不是所有推断的应用程序行为的东西.在我看来,我想要更多的控制.
这个人有一个自定义数据库初始化程序的例子,它继承自内置的CreateDatabaseIfNotExists.但是你总是可以创建自己的并实现你想要查看的任何逻辑(包括创建数据库).这再次避免了意外行为.我作为开发人员的个人偏好是密切控制这些东西,除非我只是在玩模型或测试项目.
超级简单的自定义DatabaseInitializer,没有意外行为:
namespace MyProject.Data.DatabaseInitializers
{
public class MyCustomDbInit<TContext> : IDatabaseInitializer<TContext>
where TContext : DbContext
{
public void InitializeDatabase(TContext context)
{
// Create our database if it doesn't already exist.
context.Database.CreateIfNotExists()
// Do you want to migrate to latest in your initializer? Add code here!
// Do you want to seed data in your initializer? Add code here!
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果您使用代码优先方法,禁用自动迁移并使用如上所述的自定义DatabaseInitializer,您将可以很好地控制发生的事情和时间.
我们在工作中使用这些策略并且没有问题(尽管确定这些策略确实需要一些麻烦).希望你会找到类似的成功!
我能够使用SQL CE复制您的问题,以及首先使用上面的代码使用EF代码.
奇怪的是,当我第一次使用你的代码时,它完美地运行了.为了解决您的问题,我必须删除_MigrationHistory.sdf文件中的表.
通过删除.sdf文件(我意识到这可能不是你的情况下的选项,但我会解决更远的问题)下次运行时,它创建了迁移表 - 但它仍然无法正常工作.如果您注意到,在步骤12,它最终会创建表格.
- 语句#1 SELECT [GroupBy1].[A1] AS [C1] FROM(SELECT COUNT(1)AS [A1] FROM [Dummies] AS [Extent1])AS [GroupBy1]
- 语句#2 SELECT [GroupBy1].[A1] AS [C1] FROM(SELECT COUNT(1)AS [A1] FROM [__ MigrationHistory] AS [Extent1])AS [GroupBy1]
- 语句#3 WARN:System.Data.SqlServerCe.SqlCeException(0x80004005):指定的表不存在.[__MigrationHistory]
- 语句#4 SELECT [GroupBy1].[A1] AS [C1] FROM(SELECT COUNT(1)AS [A1] FROM [__ MigrationHistory] AS [Extent1])AS [GroupBy1]
- 语句#5 WARN:System.Data.SqlServerCe.SqlCeException(0x80004005):指定的表不存在.[__MigrationHistory]
- 语句#6 SELECT [GroupBy1].[A1] AS [C1] FROM(SELECT COUNT(1)AS [A1] FROM [__ MigrationHistory] AS [Extent1])AS [GroupBy1]
- 语句#7 WARN:System.Data.SqlServerCe.SqlCeException(0x80004005):指定的表不存在.[__MigrationHistory]
- 语句#8 SELECT [GroupBy1].[A1] AS [C1] FROM(SELECT COUNT(1)AS [A1] FROM [__ MigrationHistory] AS [Extent1])AS [GroupBy1]
- 语句#9 WARN:System.Data.SqlServerCe.SqlCeException(0x80004005):指定的表不存在.[__MigrationHistory]
- 语句#10以隔离级别开始事务:可序列化
- 语句#11 CREATE TABLE [Dummies]([DummyId] [int] NOT NULL IDENTITY,[test] nvarchar,[addThis] nvarchar,CONSTRAINT [PK_Dummies] PRIMARY KEY([DummyId]))
- 语句#12 CREATE TABLE [__ MigrationHistory]([MigrationId] nvarchar NOT NULL,[CreatedOn] [datetime] NOT NULL,[Model] [image] NOT NULL,[ProductVersion] nvarchar NOT NULL,CONSTRAINT [PK___MigrationHistory] PRIMARY KEY( [MigrationId]))
- 声明#13 INSERT INTO [__ MigigrationHistory]([MigrationId],[CreatedOn],[Model],[ProductVersion])VALUES('201208101940587_InitialCreate','2012-08-10T19:40:59.055',,'4.3.1 ")
- 声明#14提交事务
- 语句#15 SELECT [GroupBy1].[A1] AS [C1] FROM(SELECT COUNT(1)AS [A1] FROM [Dummies] AS [Extent1])AS [GroupBy1]
通过在正确的位置创建表来解决问题:

一旦我这样做,下次代码运行时,一切都再次运行完美.
- 语句#1 SELECT [GroupBy1].[A1] AS [C1] FROM(SELECT COUNT(1)AS [A1] FROM [__ MigrationHistory] AS [Extent1])AS [GroupBy1]
- 声明#2 SELECT TOP(1)[c].[ProductVersion] AS [ProductVersion] FROM [__ MigrationHistory] AS [c]
- 语句#3 SELECT [GroupBy1].[A1] AS [C1] FROM(SELECT COUNT(1)AS [A1] FROM [__ MigrationHistory] AS [Extent1])AS [GroupBy1]
- 语句#4 SELECT [Extent1].[MigrationId] AS [MigrationId] FROM [__ MigrationHistory] AS [Extent1]
- 语句#5 SELECT [GroupBy1].[A1] AS [C1] FROM(SELECT COUNT(1)AS [A1] FROM [__ MigrationHistory] AS [Extent1])AS [GroupBy1]
- 语句#6 SELECT [GroupBy1].[A1] AS [C1] FROM(SELECT COUNT(1)AS [A1] FROM [__ MigrationHistory] AS [Extent1])AS [GroupBy1]
- 语句#7 SELECT TOP(1)[Project1].[C1] AS [C1],[Project1].[MigrationId] AS [MigrationId],[Project1].[Model] AS [Model] FROM(SELECT [Extent1] ] [MigrationId] AS [MigrationId],[Extent1].[CreatedOn] AS [CreatedOn],[Extent1].[Model] AS [Model],1 AS [C1] FROM [__MigrationHistory] AS [Extent1])AS [ Project1] ORDER BY [Project1].[CreatedOn] DESC
- 语句#8 SELECT [GroupBy1].[A1] AS [C1] FROM(SELECT COUNT(1)AS [A1] FROM [__ MigrationHistory] AS [Extent1])AS [GroupBy1]
- 语句#9 SELECT TOP(1)[Project1].[C1] AS [C1],[Project1].[MigrationId] AS [MigrationId],[Project1].[Model] AS [Model] FROM(SELECT [Extent1] ] [MigrationId] AS [MigrationId],[Extent1].[CreatedOn] AS [CreatedOn],[Extent1].[Model] AS [Model],1 AS [C1] FROM [__MigrationHistory] AS [Extent1])AS [ Project1] ORDER BY [Project1].[CreatedOn] DESC
- 语句#10以隔离级别开始事务:未指定
- 声明#11插入[Dummies]([test],[addThis])值(null,null);
从[Dummies]中选择[DummyId],其中[DummyId] = @@ IDENTITY
- 声明#12插入[Dummies]([test],[addThis])值(null,null);
从[Dummies]中选择[DummyId],其中[DummyId] = @@ IDENTITY
- 声明#13插入[Dummies]([test],[addThis])值(null,null);
从[Dummies]中选择[DummyId],其中[DummyId] = @@ IDENTITY
- 声明#14插入[Dummies]([test],[addThis])值(null,null);
从[Dummies]中选择[DummyId],其中[DummyId] = @@ IDENTITY
- 声明#15提交事务
- 语句#16 SELECT [GroupBy1].[A1] AS [C1] FROM(SELECT COUNT(1)AS [A1] FROM [Dummies] AS [Extent1])AS [GroupBy1]
如果放弃整个SDF并让它重新创建不是一个选项,那么我就是这样做的.
在appconfig中创建一个连接字符串(我意识到你可能想要动态连接字符串,这就是为什么它在你的代码中,但这应该是一次性的事情).我的连接字符串看起来像这样:
<connectionStrings>
<add connectionString="Data Source=MyContext.sdf;Persist Security Info=False;" name="MyContext" providerName="System.Data.SqlServerCe.4.0"/>
</connectionStrings>
Run Code Online (Sandbox Code Playgroud)
更改Context的构造函数,以便它将使用连接字符串:
public MyContext()
//: base(string.Format(@"DataSource=""{0}""", "MyContext.sdf"))
: base("MyContext")
Run Code Online (Sandbox Code Playgroud)
所有这一切都是必要的,因此您可以在Package Manager Console中运行一些命令,以便重新创建表.打开包管理器控制台,然后运行以下命令:
add-migration initial -ignorechanges
Run Code Online (Sandbox Code Playgroud)
接下来,运行程序 - 它将抛出一些警告,但随后它将为您创建表,并填充它.之后,您可以更改构造函数,我没有更多问题.
注意:一旦它开始工作,种子功能也开始工作
我尝试了您提供的代码,在本例中(使用 SQL Server 而不是 CE)并得到了以下结果。我删除了 Database.Create 代码并允许 EF 的自动迁移来完成它的工作。现在可以正确运行并调用 Seed 方法。
internal class Program
{
private static void Main()
{
EntityFrameworkProfiler.Initialize();
Database.DefaultConnectionFactory = new SqlConnectionFactory("System.Data.SqlServer");
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, MyContextConfiguration>());
using (var context = new MyContext())
{
var element = context.Dummies.FirstOrDefault();
}
}
}
internal class Dummy
{
public String Id { get; set; }
}
internal sealed class MyContext : DbContext
{
public MyContext() : base(@"Data Source=localhost;Initial Catalog=Dummies;User Id=<USER_ID>;Password=<PASSWORD>;MultipleActiveResultSets=False;")
{
}
public DbSet<Dummy> Dummies { get; set; }
}
internal sealed class MyContextConfiguration : DbMigrationsConfiguration<MyContext>
{
public MyContextConfiguration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
protected override void Seed(MyContext context)
{
context.Dummies.AddOrUpdate(new Dummy() { Id = "First" });
}
}
Run Code Online (Sandbox Code Playgroud)
如果您查看 EF 探查器,您会发现现在有更多针对数据库运行的查询(甚至检查旧的 EdmMetaData 表...这很奇怪,因为如果现在遇到该表,它应该删除该表__MigrationHistory 表的)。我不知道为什么会发生这种情况,我想这要么是我们这边的配置问题(我还不知道如何修复),要么是迁移代码中的错误。
因此,我认为对于 EF 迁移,我们要么进行基于代码的迁移(请参阅此处的我的博客文章),要么进行自动迁移(如此代码片段所示)。我想随着时间的推移,我会更好地理解为什么 EF(或我的迁移方式)有这种奇怪的行为 - 或者 EF 本身会随着它的发展而变得更好。
| 归档时间: |
|
| 查看次数: |
14077 次 |
| 最近记录: |