迁移错误:

今天在使用EF6 Code First时,出现如下错误,折腾了老半天。分享一下,帮后面的兄弟少走弯路。

PM> Enable-Migrations
Checking if the context targets an existing database...
使用“”个参数调用“CreateInstanceFrom”时发生异常:“尝试读取或写入受保护的内存。这通常指示其他内存已损坏。”
所在位置 E:\....\src\packages\EntityFramework.6.1.\tools\EntityFramework.psm1: 字符:
+ $domain.CreateInstanceFrom <<<< (
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodTargetInvocation

出现这个的原因是因为:Model项目中的EntityFrameWork的版本,和其它的项目版本不一致造成的。统一改成一样的重新编译一切正常。

本演练将提供对实体框架中 Code First 迁移的概述。您可以完成整个演练,也可以跳至自己感兴趣的主题。主题如下:

  • 启用迁移
  • 生成并运行迁移
  • 自定义迁移
  • 数据移动和自定义 SQL
  • 迁移到特定版本(包括降级)
  • 生成 SQL 脚本
  • 在应用程序启动时自动升级(MigrateDatabaseToLatestVersion 初始值设定项)

构建一个初始模型和数据库

在我们开始使用迁移之前,需要有一个项目和一个 Code First 模型。对于本次演练任务,我们仍将使用 Blog 和 Post 规范模型。

  • 创建新的 MigrationsDemo 控制台应用程序
  • 将最新版本的 EntityFramework NuGet 包添加到项目
    • 工具 –> 库程序包管理器 –> 程序包管理器控制台
    • 运行 Install-Package EntityFramework 命令
  • 添加一个包含下面所示代码的 Model.cs 文件。这段代码定义一个 Blog 类,该类由我们的域模型和一个作为 EF Code First 上下文的 BlogContext 类组成。
using System.Data.Entity; 
using System.Collections.Generic; 
using System.ComponentModel.DataAnnotations; 
using System.Data.Entity.Infrastructure; 
 
namespace MigrationsDemo 

    public class BlogContext : DbContext 
    { 
        public DbSet<Blog> Blogs { get; set; } 
    } 
 
    public class Blog 
    { 
        public int BlogId { get; set; } 
        public string Name { get; set; } 
    } 
}
  • 现在我们已经有了一个模型,该使用它来执行数据访问了。用下面所示代码更新 Program.cs 文件。
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
 
namespace MigrationsDemo 

    class Program 
    { 
        static void Main(string[] args) 
        { 
            using (var db = new BlogContext()) 
            { 
                db.Blogs.Add(new Blog { Name = "Another Blog " }); 
                db.SaveChanges(); 
 
                foreach (var blog in db.Blogs) 
                { 
                    Console.WriteLine(blog.Name); 
                } 
            } 
 
            Console.WriteLine("Press any key to exit..."); 
            Console.ReadKey(); 
        } 
    } 
}
  • 运行应用程序,您会看到已为您创建 MigrationsCodeDemo.BlogContext 数据库。

    如果安装了 SQL Express(包括在 Visual Studio 2010 中),则该数据库将在本地 SQL Express 实例(.\SQLEXPRESS)中创建。如果未安装 SQL Express,则 Code First 将尝试使用 LocalDb ((localdb)\v11.0) - LocalDb 包括在 Visual Studio 2012 中。

    注意:如果安装有 SQL Express,则始终优先使用它,即使您在使用 Visual Studio 2012 也是如此


    (LocaDb 数据库)


    (SQL Express 数据库)

启用迁移

现在要对我们的模型进行一些更改。

  • 让我们向 Blog 类引入一个 Url 属性。
public string Url { get; set; }

如果您打算再次运行该应用程序,则将显示 InvalidOperationException,表明支持“BlogContext”上下文的模型已在数据库创建后发生更改。请考虑使用 Code First 迁移更新数据库 ( http://go.microsoft.com/fwlink/?LinkId=238269)。

按异常消息指示,现在应该开始使用 Code First 迁移。第一步是为上下文启用迁移。

  • 在程序包管理器控制台中运行 Enable-Migrations 命令

此命令已为项目添加了 Migrations 文件夹,此新文件夹包含两个文件:

  • Configuration 类。此类允许您针对上下文配置迁移的行为。对于此演练,我们将使用默认配置。
    因为在您的项目中只有一个 Code First 上下文,所以 Enable-Migrations 已自动填入要应用此配置的上下文类型中。
  • InitialCreate 迁移。此迁移已在启用迁移之前生成,因为我们事先让 Code First 自动创建了一个数据库。此基架迁移中的代码表示数据库中已创建的对象。在本例中,此类对象为 Blog 表,其中包含 BlogId 和 Name 列。文件名包含时间戳,这对于排序十分有帮助。
    如果尚未创建数据库,则不会将此 InitialCreate 迁移添加到项目中。而是,首次调用 Add-Migration 时,用于创建这些表的代码将为新迁移搭建基架。

生成并运行迁移

Code First 迁移有两个主命令,下面您将会熟悉它们。

  • Add-Migration 将根据自创建上次迁移以来您对模型所做的更改,为下一次迁移搭建基架。
  • Update-Database 将所有挂起的迁移应用于数据库。

我们需要为迁移搭建基架以处理先前添加的新 Url 属性。使用 Add-Migration 命令可以为这些迁移指定名称,我们将其称为 AddBlogUrl

  • 在程序包管理器控制台中运行 Add-Migration AddBlogUrl 命令。
  • 在 Migrations 文件夹中,现在有了新的 AddBlogUrl 迁移。该迁移文件名以时间戳作为前缀,这对于排序十分有帮助。
namespace MigrationsDemo.Migrations 

    using System; 
    using System.Data.Entity.Migrations; 
     
    public partial class AddBlogUrl : DbMigration 
    { 
        public override void Up() 
        { 
            AddColumn("Blogs", "Url", c => c.String()); 
        } 
         
        public override void Down() 
        { 
            DropColumn("Blogs", "Url"); 
        } 
    } 
}

现在,我们可以编辑此迁移或向其添加内容,一切都很不错。让我们使用 Update-Database 将此迁移应用于数据库。

  • 在程序包管理器控制台中运行 Update-Database 命令。
  • Code First 迁移将对 Migrations 文件夹中的迁移与已应用于数据库的迁移进行比较。它将了解到需要应用 AddBlogUrl 迁移,于是便运行该迁移。

此时,MigrationsDemo.BlogContext 数据库已进行了更新,其 Blogs 表中包含了 Url 列。

自定义迁移

到目前为止,我们生成并运行了迁移,而未进行任何更改。现在,让我们看一下如何编辑默认情况下生成的代码。

  • 我们需要对模型再进行一些更改,让我们向 Blog 类添加一个新的 Rating 属性。
public int Rating { get; set; }
  • 再添加一个新的 Post 类
public class Post 

    public int PostId { get; set; } 
    [MaxLength(200)] 
    public string Title { get; set; } 
    public string Content { get; set; } 
 
    public int BlogId { get; set; } 
    public Blog Blog { get; set; } 
}
  • 我们还要向 Blog 类添加一个 Posts 集合,以在 Blog 与 Post 之间形成另一层关系。
public virtual List<Post> Posts { get; set; }

我们将使用 Add-Migration 命令让 Code First 迁移自动在迁移时为其最佳猜测搭建基架。我们将此迁移称为 AddPostClass

  • 在程序包管理器控制台中运行 Add-Migration AddPostClass 命令。

Code First 迁移为这些更改搭建基架的工作做得很好,但有些内容可能需要我们更改:

  1. 首先,我们向 Posts.Title 列添加一个唯一索引
    (添加到以下代码的第 22 和 29 行)。
  2. 还添加一个不可为 Null 的 Blogs.Rating 列。如果表中有任何现有数据,则这些数据将被分配采用新列数据类型的 CLR 默认值(Rating 为整数,因此默认值将为 0)。但我们想指定默认值 3,为 Blogs 表中的现有行设置一个还不错的起始等级。
    (您可以在以下代码的第 24 行看到指定的默认值)
namespace MigrationsCodeDemo.Migrations 

    using System; 
    using System.Data.Entity.Migrations; 
 
    public partial class AddPostClass : DbMigration 
    { 
        public override void Up() 
        { 
            CreateTable( 
                "Posts", 
                c => new 
                    { 
                        PostId = c.Int(nullable: false, identity: true), 
                        Title = c.String(maxLength: 200), 
                        Content = c.String(), 
                        BlogId = c.Int(nullable: false), 
                    }) 
                .PrimaryKey(t => t.PostId) 
                .ForeignKey("Blogs", t => t.BlogId, cascadeDelete: true) 
                .Index(t => t.BlogId) 
                .Index(p => p.Title, unique: true); 
 
            AddColumn("Blogs", "Rating", c => c.Int(nullable: false, defaultValue: 3)); 
        } 
 
        public override void Down() 
        { 
            DropIndex("Posts", new[] { "Title" }); 
            DropIndex("Posts", new[] { "BlogId" }); 
            DropForeignKey("Posts", "BlogId", "Blogs"); 
            DropColumn("Blogs", "Rating"); 
            DropTable("Posts"); 
        } 
    } 
}

我们编辑好的迁移已准备就绪,让我们使用 Update-Database 更新数据库。这次指定 –Verbose 标记,以便您能够看见 Code First 迁移所运行的 SQL。

  • 在程序包管理器控制台中运行 Update-Database –Verbose 命令。

数据移动/自定义 SQL

到目前为止,我们了解了不更改或迁移任何数据的迁移操作,现在看一下需要来回移动一些数据的迁移操作。目前还没有为数据移动提供本机支持,但我们可以在脚本中的任何位置运行一些任意 SQL 命令。

  • 现在向模型中添加一个 Post.Abstract 属性。随后,我们将使用 Content 列开头的一些文本为现有文章预填充 Abstract
public string Abstract { get; set; }

我们将使用 Add-Migration 命令让 Code First 迁移自动在迁移时为其最佳猜测搭建基架。

  • 在程序包管理器控制台中运行 Add-Migration AddPostAbstract 命令。
  • 生成的迁移负责处理架构更改,但我们还希望使用每篇文章内容的前 100 个字符预填充 Abstract 列。为此,可以向下拖到 SQL 并在添加该列后运行 UPDATE 语句。
    (添加到以下代码的第 12 行)。
namespace MigrationsCodeDemo.Migrations 

    using System; 
    using System.Data.Entity.Migrations; 
     
    public partial class AddPostAbstract : DbMigration 
    { 
        public override void Up() 
        { 
            AddColumn("Posts", "Abstract", c => c.String()); 
 
            Sql("UPDATE Posts SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL"); 
        } 
         
        public override void Down() 
        { 
            DropColumn("Posts", "Abstract"); 
        } 
    } 
}

我们编辑好的迁移很不错,让我们使用 Update-Database 更新数据库。我们将指定 –Verbose 标记,以便可以查看正在对数据库运行的 SQL。

  • 在程序包管理器控制台中运行 Update-Database –Verbose 命令。

迁移到特定版本(包括降级)

到目前为止,我们一直是升级到最新迁移,但有时您可能需要升级/降级到特定迁移。

假如我们希望将数据库迁移到当时运行 AddBlogUrl 迁移后所处的状态。可以使用 –TargetMigration 开关降级到此迁移。

  • 在程序包管理器控制台中运行 Update-Database –TargetMigration: AddBlogUrl 命令。

此命令将为 AddBlogAbstract 和 AddPostClass 迁移运行 Down 脚本。

如果要一直回滚到空数据库,可以使用 Update-Database –TargetMigration: $InitialDatabase 命令。

获取 SQL 脚本

假如其他开发人员的计算机上也需要这些更改,则在我们将所做的更改签入源代码管理后,他们只需执行同步操作即可。在他们得到我们的新迁移后,即可运行 Update-Database 命令在本地应用这些更改。不过,如果希望将这些更改推送到测试服务器并最终应用于生产,我们可能希望向 DBA 提交一个 SQL 脚本。

  • 运行 Update-Database 命令,但此时指定 –Script 标记,使更改写入脚本而不应用。我们还将指定为其生成脚本的源和目标迁移。我们希望脚本用于从空数据库 ($InitialDatabase) 最新版本(迁移 AddPostAbstract)的迁移。
    如果不希望指定目标迁移,迁移将使用最新迁移作为目标。如果未指定源迁移,迁移将使用数据库的当前状态。
  • 在程序包管理器控制台中运行 Update-Database -Script -SourceMigration: $InitialDatabase -TargetMigration: AddPostAbstract 命令。

Code First 迁移将运行迁移管道,而不是实际应用更改,它会自动将更改写出到一个 .sql 文件。生成脚本后,将会自动在 Visual Studio 中打开它,以供您查看或保存。

在应用程序启动时自动升级(MigrateDatabaseToLatestVersion 初始值设定项)

如果您要部署应用程序,可能希望在应用程序启动时自动升级数据库(通过应用所有挂起的迁移)。可通过注册 MigrateDatabaseToLatestVersion 数据库初始值设定项来实现这一点。数据库初始值设定项只是包含用于确保数据库安装正确的某种逻辑。首次在应用程序进程 (AppDomain) 中使用上下文时,将运行此逻辑。

我们可以更新 Program.cs 文件(如下所示),先设置 BlogContext 的 MigrateDatabaseToLatestVersion 初始值设定项,然后再使用上下文(第 14 行)。请注意,您还需要为 System.Data.Entity 命名空间添加一个 using 语句(第 5 行)。

创建此初始值设定项的实例时,需要指定上下文类型 (BlogContext) 和迁移配置 (Configuration) - 迁移配置是启用迁移时添加到 Migrations 文件夹的类。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Data.Entity; 
using MigrationsDemo.Migrations; 
 
namespace MigrationsDemo 

    class Program 
    { 
        static void Main(string[] args) 
        { 
            Database.SetInitializer(new MigrateDatabaseToLatestVersion<BlogContext, Configuration>()); 
 
            using (var db = new BlogContext()) 
            { 
                db.Blogs.Add(new Blog { Name = "Another Blog " }); 
                db.SaveChanges(); 
 
                foreach (var blog in db.Blogs) 
                { 
                    Console.WriteLine(blog.Name); 
                } 
            } 
 
            Console.WriteLine("Press any key to exit..."); 
            Console.ReadKey(); 
        } 
    } 
}

现在,每次应用程序运行时,它都会先检查所面向的数据库是否是最新的;如果不是,便会应用所有挂起的迁移。

来源:https://msdn.microsoft.com/zh-cn/data/jj591621(v=vs.113).aspx

Code First 迁移,及迁移错误的更多相关文章

  1. EF 中 Code First 的数据迁移以及创建视图

    写在前面: EF 中 Code First 的数据迁移网上有很多资料,我这份并没什么特别.Code First 创建视图网上也有很多资料,但好像很麻烦,而且亲测好像是无效的方法(可能是我太笨,没搞成功 ...

  2. MVC5中Model层开发数据注解 EF Code First Migrations数据库迁移 C# 常用对象的的修饰符 C# 静态构造函数 MSSQL2005数据库自动备份问题(到同一个局域网上的另一台电脑上) MVC 的HTTP请求

    MVC5中Model层开发数据注解   ASP.NET MVC5中Model层开发,使用的数据注解有三个作用: 数据映射(把Model层的类用EntityFramework映射成对应的表) 数据验证( ...

  3. Entity Framework应用:Code First模式数据迁移的基本用法

    使用Entity Framework的Code First模式在进行数据迁移的时候会遇到一些问题,熟记一些常用的命令很重要,下面整理出了数据迁移时常用的一些命令. 一.模型设计 EF默认使用id字段作 ...

  4. EF core (code first) 通过自动迁移实现多租户数据分离 :按Schema分离数据

    前言 本文是多租户系列文章的附加操作文章,如果想查看系列中的其他文章请查看下列文章 主线文章 Asp.net core下利用EF core实现从数据实现多租户(1) Asp.net core下利用EF ...

  5. Saiku数据库迁移H2迁移到Mysql(二十二)

    Saiku数据库迁移H2迁移到Mysql Saiku默认使用H2数据库来存储saiku的用户与角色信息,我们可以根据角色来做saiku的权限控制,然后将角色分配给用户 ,该用户就会有对应的约束了! 由 ...

  6. Entity Framework 学习系列(3) - MySql Code First 开发方式+数据迁移

    目录 # 写在前面 一.开发环境 二.创建项目 三.安装程序包 四.创建模型 五.连接字符串 六.编辑程序 七.数据迁移 写在最后 # 写在前面 这几天,一直都在学习Entity Framework ...

  7. EF Code First Migrations数据库迁移

    1.EF Code First创建数据库 新建控制台应用程序Portal,通过程序包管理器控制台添加EntityFramework. 在程序包管理器控制台中执行以下语句,安装EntityFramewo ...

  8. C# EF Code First Migrations数据库迁移

    1.EF Code First创建数据库 新建控制台应用程序Portal,通过程序包管理器控制台添加EntityFramework. 在程序包管理器控制台中执行以下语句,安装EntityFramewo ...

  9. EF Code First Migrations数据库迁移 (转帖)

    1.EF Code First创建数据库 新建控制台应用程序Portal,通过程序包管理器控制台添加EntityFramework. 在程序包管理器控制台中执行以下语句,安装EntityFramewo ...

随机推荐

  1. [板子]倍增LCA

    倍增LCA板子,没有压行,可读性应该还可以.转载请随意. #include <cstdio> #include <cstring> #include <algorithm ...

  2. C++11中的std::function

    看看这段代码 先来看看下面这两行代码: std::function<void(EventKeyboard::KeyCode, Event*)> onKeyPressed; std::fun ...

  3. Redis的三种启动方式

    转载:http://www.tuicool.com/articles/aQbQ3u Part I. 直接启动 下载 官网下载 安装 tar zxvf redis-2.8.9.tar.gz cd red ...

  4. 搭建web框架手册(一)

    昨天听完永康对EASYUI的介绍后终于明白了优秀的UI框架就是第一生产力,过去自己一直沉浸在后端代码中,完全忽视了前端的生产力交互,总觉得界面漂亮就是生产力,其实大错特错,真正的具有高效生产力的界面其 ...

  5. Markdown语法 中文版

    文章翻译自Markdown创始人JOHN GRUBER的 个人博客, 英文原文请参见 Markdown Syntax; 本文地址: http://www.cnblogs.com/ayning/p/43 ...

  6. 取消ie浏览器edge浏览器输入框右边的叉和眼睛

    在ie高版本浏览器和edge浏览器里type为text和password的input框在输入时右边会出现×和眼睛,如果需要清除,方法如下: 首先在页面头部声明兼容性模式 <meta http-e ...

  7. Nike Zoom Winflo 2 Kvinder Sko Når jeg set elementet

    De fleste af os elskede denne Nike Pegasus 34 foruden var ved at blive begejstret for at få dine ben ...

  8. Linux下Redis的安装和部署

    一.Redis介绍 Redis是当前比较热门的NOSQL系统之一,它是一个key-value存储系统.和Memcache类似,但很大程度补偿了Memcache的不足,它支持存储的value类型相对更多 ...

  9. Linux学习笔记<五>

    管道命令(pipe) 1.把一个命令的输出作为另一个命令的输入 ls -al /etc | less 2.选取命令:cut和grep cut命令可以将一段消息的某段切出来. -d接分隔符,-f是取出第 ...

  10. React的井字过三关(3)

    这是React井字棋项目的最后一篇笔记,记述AI实现. 一. 是开头都会说的原理 但凡懂一点围棋的人都知道"大场"这个概念,可以浅显地把它理解为布局时棋盘上各处的要点.棋谚&quo ...