为什么要改用code first

用过code first的基本上都不会再想用回model first或是db first(谁用谁知道)。不要问我为什么不一开始就直接使用code first,因为那个时候我还不会(甚至还把model first当成了code first)。

因为工作中使用的就是code first,且越用越习惯,越用越喜欢。

原因如果:

  • 再也用为每次生成那个笨重的edmx文件性急了
  • 再也不用当心保存tt文件而丢失特性、注销、扩展方法了
  • 再也不用为了使用微软的验证插件非得写Metadata文件了
  • 再也不用为了扩展tt文件生成的实体类去写(partial)部分类了。
  • 再也不用为了生成满足自己需要的实体而去修改那些坑爹的tt文件里面的语法代码了(如:默认每个实体继承一个父类)
  • 再也不用为了查找edmx文件打不开,去编辑庞大的edmx文件中找那些坑爹的错误了。
  • 等等还有些暂时没想到的....

说改就改

修改前实体:db first(由tt文件生成)

修改后实体:code first(完全手写)

然后把实体更新到数据库对应的表结构。执行命令Enable-Migrations

遇到问题:

The EntityFramework package is not installed on project ''.(原因:因为没有选择“默认项目”)

继续问题:

The project 'Blogs.Model' failed to build.(原因:没有建一个继承于DbContext的类)

ok,提示已经启用迁移。

然后我们执行命令:Add-Migration blogs

异常: 从数据库中获取提供程序信息时出错。这可能是 Entity Framework 使用的连接字符串不正确导致的。有关详细信息,请查看内部异常并确保连接字符串正确。

我的乖乖,我非常确定我们字符串链接是正确的啊。

最后确定忘记给数据连接上下文在构造函数中传入配置文件的数据库链接名。

  public BlogDbContext()
: base("HiBlogsTest")
{
}

再执行(Add-Migration blogs),再出错:

异常:无法加载指定的元数据资源。(百度之,原来是链接字符串有问题。http://www.cnblogs.com/chengxiaohui/articles/2106765.html)

 <add name="HiBlogsTest" connectionString="metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;
provider connection string=&quot;
data source=.;
initial catalog=HiBlogsTest;
user id=sa;
password=123qwe;
MultipleActiveResultSets=True;
App=EntityFramework&quot;" providerName="System.Data.EntityClient" />

改成:(那一堆csdl、ssdl、msl什么都不要了,就留个简单的链接。干净)

<add name="HiBlogsTest" connectionString="Data Source=.;Initial Catalog=HiBlogsTest;User ID=sa;Password=123qwe;" providerName="System.Data.SqlClient" />

ok,终于没有看见红色的字了。

且看到了一个自动生成的blogs文件。且不管,看看数据库是否有表结构。

空空如也。(屁都没看到一个)(原因:BlogDbContext上下文中没有添加实体,没有告诉程序要生成哪些实体到数据库)

给BlogDbContext类添加数据代码:

  public class BlogDbContext : DbContext
{
public BlogDbContext()
: base("HiBlogsTest")
{
}
public DbSet<BlogInfo> BlogInfos { get; set; }
public DbSet<BlogComment> BlogComments { get; set; }
public DbSet<BlogReadInfo> BlogReadInfos { get; set; }
public DbSet<BlogTag> BlogTags { get; set; }
public DbSet<BlogType> BlogTypes { get; set; }
public DbSet<BlogUser> BlogUsers { get; set; }
public DbSet<BlogUserInfo> BlogUserInfos { get; set; }
}

然后执行 :Add-Migration blogs 再执行 update-database

终于看到表数据了。

有了表还不行,我们还没有主外键。

修改BlogDbContext如下:

public class BlogDbContext : DbContext
{
public BlogDbContext()
: base("HiBlogsTest")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder); var entityBlogUser = modelBuilder.Entity<BlogUser>(); entityBlogUser.HasMany(p => p.BlogInfos).WithRequired(t => t.BlogUser)
.Map(m => m.MapKey("BlogUserId")); entityBlogUser.HasRequired(p => p.BlogUserInfo).WithRequiredPrincipal(t => t.BlogUser)
.Map(m => m.MapKey("BlogUserId")); entityBlogUser.HasMany(p => p.BlogTags).WithRequired(t => t.BlogUser)
.Map(m => m.MapKey("BlogUserId")); entityBlogUser.HasMany(p => p.BlogTypes).WithRequired(t => t.BlogUser)
.Map(m => m.MapKey("BlogUserId")); entityBlogUser.HasMany(p => p.BlogComments).WithRequired(t => t.BlogUser)
.Map(m => m.MapKey("BlogUserId")); var entityBlogInfo = modelBuilder.Entity<BlogInfo>(); entityBlogInfo.HasMany(p => p.BlogTags).WithMany(t => t.BlogInfos)
.Map(m => m.ToTable("BlogInfo_BlogTag")); entityBlogInfo.HasMany(p => p.BlogTypes).WithMany(t => t.BlogInfos)
.Map(m => m.ToTable("BlogInfo_BlogType")); entityBlogInfo.HasMany(p => p.BlogComments).WithRequired(t => t.BlogInfo)
.Map(m => m.MapKey("BlogInfoId")); entityBlogInfo.HasMany(p => p.BlogReadInfos).WithRequired(t => t.BlogInfo)
.Map(m => m.MapKey("BlogInfoId"));
} public DbSet<BlogInfo> BlogInfos { get; set; }
public DbSet<BlogComment> BlogComments { get; set; }
public DbSet<BlogReadInfo> BlogReadInfos { get; set; }
public DbSet<BlogTag> BlogTags { get; set; }
public DbSet<BlogType> BlogTypes { get; set; }
public DbSet<BlogUser> BlogUsers { get; set; }
public DbSet<BlogUserInfo> BlogUserInfos { get; set; }
}

然后重新命令:Add-Migration blogs 再执行 update-database

又见错误:

将 FOREIGN KEY 约束 'FK_dbo.BlogInfo_dbo.BlogUser_BlogUserId' 引入表 'BlogInfo' 可能会导致循环或多重级联路径。请指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。
无法创建约束。请参阅前面的错误消息。

于是,一个一个的外键删掉,又一个个的来建。终于发现:(下图是数据库关系图,mssql生成的)

百度之:(原来是为了约束联级删除数据做的约束。实在话,还没玩过联级删除了,说明这个需求应该不是很常用。找个方法禁用可否?)

直接加一个.WillCascadeOnDelete(false)就可以了。(http://www.cnblogs.com/chear/archive/2012/11/09/2762145.html)

public class BlogDbContext : DbContext
{
public BlogDbContext()
: base("HiBlogsTest")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder); var entityBlogUser = modelBuilder.Entity<BlogUser>(); entityBlogUser.HasMany(p => p.BlogInfos).WithRequired(t => t.BlogUser)
.Map(m => m.MapKey("BlogUserId")).WillCascadeOnDelete(false);
//与上面等效
//modelBuilder.Entity<BlogInfo>().HasRequired(p => p.BlogUser).WithMany(t => t.BlogInfos) //以BlogUser为主表(BlogUserInfo为从表,建立外键)
entityBlogUser.HasRequired(p => p.BlogUserInfo).WithRequiredPrincipal(t => t.BlogUser)
.Map(m => m.MapKey("BlogUserId")).WillCascadeOnDelete(false);
//等效于HasRequired(p => ).WithOptional(i => ); ////以BlogUserInfo为主表(BlogUser为从表,建立外键)
//modelBuilder.Entity<BlogUser>().HasRequired(p => p.BlogUserInfo).WithRequiredDependent(t => t.BlogUser)
//.Map(m => m.MapKey("BlogUserId")).WillCascadeOnDelete(false);
//等效于 HasOptional(p => ).WithRequired(i => ); entityBlogUser.HasMany(p => p.BlogTags).WithRequired(t => t.BlogUser)
.Map(m => m.MapKey("BlogUserId")).WillCascadeOnDelete(false); entityBlogUser.HasMany(p => p.BlogTypes).WithRequired(t => t.BlogUser)
.Map(m => m.MapKey("BlogUserId")).WillCascadeOnDelete(false); entityBlogUser.HasMany(p => p.BlogComments).WithRequired(t => t.BlogUser)
.Map(m => m.MapKey("BlogUserId")).WillCascadeOnDelete(false); var entityBlogInfo = modelBuilder.Entity<BlogInfo>(); entityBlogInfo.HasMany(p => p.BlogTags).WithMany(t => t.BlogInfos)
.Map(m => m.ToTable("BlogInfo_BlogTag")); entityBlogInfo.HasMany(p => p.BlogTypes).WithMany(t => t.BlogInfos)
.Map(m => m.ToTable("BlogInfo_BlogType")); entityBlogInfo.HasMany(p => p.BlogComments).WithRequired(t => t.BlogInfo)
.Map(m => m.MapKey("BlogInfoId")).WillCascadeOnDelete(false); entityBlogInfo.HasMany(p => p.BlogReadInfos).WithRequired(t => t.BlogInfo)
.Map(m => m.MapKey("BlogInfoId")).WillCascadeOnDelete(false);
} public DbSet<BlogInfo> BlogInfos { get; set; }
public DbSet<BlogComment> BlogComments { get; set; }
public DbSet<BlogReadInfo> BlogReadInfos { get; set; }
public DbSet<BlogTag> BlogTags { get; set; }
public DbSet<BlogType> BlogTypes { get; set; }
public DbSet<BlogUser> BlogUsers { get; set; }
public DbSet<BlogUserInfo> BlogUserInfos { get; set; }
}

然后重新命令:Add-Migration blogs 再执行 update-database

完美,表结构过来了。表关系过来了。(接下来就是该代码了,因为表名做了小的改动,字段也做了少许调整所以改的东西还真不少。整整改了一天时间。)

现在回过头来想想,之前是先model first之后小许改动就用的db first。以前怎么没有遇到过(将 FOREIGN KEY 约束 'FK_dbo.BlogInfo_dbo.BlogUser_BlogUserId' 引入表 'BlogInfo' 可能会导致循环或多重级联路径。请指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。
无法创建约束。请参阅前面的错误消息。)这个错误。好奇心驱使,觉得看看以前的代码的edmx是怎么管理这种关系的。

很惊奇的发现,完全没有问题。于是,不死心看看数据库里面是不是有什么蹊跷。

搜噶,原来如此。通过model first生成的主外键关系默认就没有设计联级删除,而code first默认设置就是联级删除。

以上内容,都是我胡说八道。谢谢您的阅读,希望对您有那么一点点作用。

Hi-Blogs源码地址:http://git.oschina.net/zhaopeiym/Hi-Blogs

最近因为工作实在太慢,开源博客长久没有更新。今天突然来回翻了好几遍,发现半年前的自己写的代码是如此的不堪入目。

今天仅仅只是把db first改成了code first,发霉的代码我还得找个时间好好重构重构。

首发地址:http://www.cnblogs.com/zhaopei/p/5540532.html

一步步开发自己的博客 .NET版(9、从model first替换成code first 问题记录)的更多相关文章

  1. 一步步开发自己的博客 .NET版(1、基本显示)

    前言 我们每个猿都有一个搭建自己独立博客的梦,我也不例外.以前想 现在想 以后也想.之所以一直迟迟没有着手,是因为难以跨出第一步.每次心里想着,等我以后技术好了再说,然后就没有然后了.以前用过word ...

  2. 一步步开发自己的博客 .NET版(3、注册登录功能)

    前言 这次开发的博客主要功能或特点:    第一:可以兼容各终端,特别是手机端.    第二:到时会用到大量html5,炫啊.    第三:导入博客园的精华文章,并做分类.(不要封我)    第四:做 ...

  3. 一步步开发自己的博客 .NET版(4、文章发布功能)百度编辑器

    前言 这次开发的博客主要功能或特点: 第一:可以兼容各终端,特别是手机端. 第二:到时会用到大量html5,炫啊. 第三:导入博客园的精华文章,并做分类.(不要封我) 第四:做个插件,任何网站上的技术 ...

  4. 一步步开发自己的博客 .NET版(5、Lucenne.Net 和 必应站内搜索)

    前言 这次开发的博客主要功能或特点:    第一:可以兼容各终端,特别是手机端.    第二:到时会用到大量html5,炫啊.    第三:导入博客园的精华文章,并做分类.(不要封我)    第四:做 ...

  5. 一步步开发自己的博客 .NET版 剧终篇(6、响应式布局 和 自定义样式)

    前言 这次开发的博客主要功能或特点:    第一:可以兼容各终端,特别是手机端.    第二:到时会用到大量html5,炫啊.    第三:导入博客园的精华文章,并做分类.(不要封我)    第四:做 ...

  6. 一步步开发自己的博客 .NET版(11、Web.config文件的读取和修改)

    Web.config的读取 对于Web.config的读取大家都很属性了.平时我们用得比较多的就是appSettings节点下配置.如: 我们对应的代码是: = ConfigurationManage ...

  7. 一步步开发自己的博客 .NET版(10、前端对话框和消息框的实现)

    关于前端对话框.消息框的优秀插件多不胜数.造轮子是为了更好的使用轮子,并不是说自己造的轮子肯定好.所以,这个博客系统基本上都是自己实现的,包括日志记录.响应式布局.评论功能等等一些本可以使用插件的.好 ...

  8. ASP.NET 一步步开发自己的博客 .NET版(11、Web.config文件的读取和修改)

    原文:http://www.cnblogs.com/zhaopei/p/5677053.html

  9. 一步步搭建自己的博客 .NET版(2、评论功能)

    前言 这次开发的博客主要功能或特点:    第一:可以兼容各终端,特别是手机端.    第二:到时会用到大量html5,炫啊.    第三:导入博客园的精华文章,并做分类.(不要封我)    第四:做 ...

随机推荐

  1. NodeJs之child_process

    一.child_process child_process是NodeJs的重要模块.帮助我们创建多进程任务,更好的利用了计算机的多核性能. 当然也支持线程间的通信. 二.child_process的几 ...

  2. SQLSERVER走起微信公众帐号全新改版 全新首页

    SQLSERVER走起微信公众帐号全新改版 全新首页 今天,SQLSERVER走起微信公众帐号增加了首页功能 虽然还是订阅号,不过已经对版面做了比较大的修改,希望各位亲用得放心.用得安心O(∩_∩)O ...

  3. 基于本地存储的kvm虚拟机在线迁移

    基于本地存储的kvm虚拟机在线迁移 kvm虚拟机迁移分为4种(1)热迁移基于共享存储(2)热迁移基于本地存储(3)冷迁移基于共享存储(4)冷迁移基于本地存储 这里介绍的是基于本地存储的热迁移 动态块迁 ...

  4. iOS二维码生成、识别、扫描等

    二维码扫描 前言: 最近的项目中使用到了二维码,二维码这个模块功能也完成:觉得还是有必要总结一下用来做记录.好长时间没有写二维码了都忘记在差不多了,重新拾起来还是挻快的. 二维码使用场景: 生活中有很 ...

  5. Ubuntu 16.10 开启PHP错误提示

    两个步骤: 修改php.ini配置文件中的error_reporting 和 display_errors两地方内容: sudo vim /etc/php/7.0/apache2/php.ini er ...

  6. 23种设计模式--单例模式-Singleton

    一.单例模式的介绍 单例模式简单说就是掌握系统的至高点,在程序中只实例化一次,这样就是单例模式,在系统比如说你是该系统的登录的第多少人,还有数据库的连接池等地方会使用,单例模式是最简单,最常用的模式之 ...

  7. C#基础篇 - 正则表达式入门

    1.基本概念 正则表达式(Regular Expression)就是用事先定义好的一些特定字符(元字符)或普通字符.及这些字符的组合,组成一个“规则字符串”,这个“规则字符串”用来判断我们给定的字符串 ...

  8. 深入研究Visual studio 2017 RC新特性

    在[Xamarin+Prism开发详解三:Visual studio 2017 RC初体验]中分享了Visual studio 2017RC的大致情况,同时也发现大家对新的Visual Studio很 ...

  9. Android如何制作漂亮的自适布局的键盘

    最近做了个自定义键盘,但面对不同分辨率的机型其中数字键盘不能根据界面大小自已铺满,但又不能每种机型都做一套吧,所以要做成自适应,那这里主讲思路. 这里最上面的titlebar高度固定,下面输入的金额高 ...

  10. JSP 标准标签库(JSTL)

    JSP 标准标签库(JSTL) JSP标准标签库(JSTL)是一个JSP标签集合,它封装了JSP应用的通用核心功能. JSTL支持通用的.结构化的任务,比如迭代,条件判断,XML文档操作,国际化标签, ...