Code Frist实体关系设计--迁移数据库--初始化数据

1.写作背景

在写这篇文章前,本想继续补充完前面介绍到 WebApi 知识点,但下面的讲解,要进行 实体/模型设计 ,并对数据进行 CRUD 实际操作了,所以先介绍一下EF基本使用啦!

本章为何不以最新版的EF7( 下一章也会介绍它 )先讲?

一是EF7还处于beta阶段,功能也没有开发完毕,生产环境应用有诸多问题;

二是EF7想比之前的版本变化还是很多的,我们先从代码对比上了解,以便你以后从EF6升级到EF7;

三是EF6目前也不能在ASP.NET 5类型项目中使用,但它对ASP.NET 5以外的类型项目非常好用了,这个要给赞的。

本章对EF6前身后世,不做过多细述,我这次总结学习的系列写文章,毕竟是针对 ASP.NET 5 技术开展的。

2.创建项目

注: 以下代码,在传统的web等项目类型中也是适用。但讲解以 老版的控制台 项目:

3.实体类

一对多 关系: 角色Role:用户User=1:N

Role.cs代码:

using System.Collections.Generic;

namespace EF6.Consoles.Models
{
    /// <summary>
    /// 角色实体
    /// </summary>
    public class Role
    {
        public int Id { get; set; }
        public string RoleName { get; set; }

        /// <summary>
        /// 用户实体集合 (导航属性)
        /// </summary>
        public ICollection<User> Users { get; set; }
    }
}

User.cs代码:

namespace EF6.Consoles.Models
{
    /// <summary>
    /// 用户实体
    /// </summary>
    public class User
    {
        public int Id { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }

        /// <summary>
        /// 角色Id (外键)
        /// </summary>
        public int RoleId { get; set; }
        /// <summary>
        /// 角色实体 (导航属性)
        /// </summary>
        public Role Role { get; set; }
    }
}

多对多 关系:类别Category:产品Product=N:M

Category.cs代码:

using System.Collections.Generic;

namespace EF6.Consoles.Models
{
    /// <summary>
    /// 类别实体
    /// </summary>
    public class Category
    {
        public int Id { get; set; }
        public string CategoryName { get; set; }

        /// <summary>
        /// 产品实体集合 (导航属性)
        /// </summary>
        public ICollection<Product> Products { get; set; }
    }
}

Product.cs代码:

using System.Collections.Generic;

namespace EF6.Consoles.Models
{
    /// <summary>
    /// 产品实体
    /// </summary>
    public class Product
    {
        public int Id { get; set; }
        public string ProductName { get; set; }

        /// <summary>
        /// 类别实体集合 (导航属性)
        /// </summary>
        public ICollection<Category> Categories { get; set; }
    }
}

注: 外键(含左右外键)可以为空的关系 ,就不举例了;其次实体属性(数据) 特性注解 在下一章EF7再说。实体配置还有 Fluent API 方式。

4.安装EF

5.数据库上下文

EFContext.cs代码:

using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace EF6.Consoles.Models
{
    public class EFContext : DbContext
    {
        public EFContext() : base("EFContext") { }

        public DbSet<Role> Roles { get; set; }
        public DbSet<User> Users { get; set; }
        public DbSet<Category> Categories { get; set; }
        public DbSet<Product> Products { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

            //也可以不设置,也会默认
            modelBuilder.Entity<Category>()
                .HasMany(c => c.Products).WithMany(p => p.Categories)
                .Map(t => t.MapLeftKey("CategoryId")
                .MapRightKey("ProductId")
                .ToTable("CategoryProduct"));
        }
    }
}

6.数据库连接字符串

在 App.config 中 <configuration> 节点内添加数据库连接字符串 :

  <connectionStrings>
    <add name="EFContext" connectionString="Data Source=.;Initial Catalog=TestDB;UID=sa;PWD=123456"
      providerName="System.Data.SqlClient" />
  </connectionStrings>

7.手动迁移

查看迁移帮助

启用迁移

此时生成目录及文件:

修改 Configuration.cs 代码:

using EF6.Consoles.Models;
using System.Collections.Generic;
using System.Data.Entity.Migrations;
using System.Linq;

namespace EF6.Consoles.Migrations
{
    internal sealed class Configuration : DbMigrationsConfiguration<EFContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
        }

        protected override void Seed(EFContext db)
        {
            var roles = new List<Role>
            {
                new Role() {  RoleName="管理员"},
                new Role() {  RoleName="会员"}
            };
            roles.ForEach(list => db.Roles.AddOrUpdate(r => r.RoleName, list));
            db.SaveChanges();

            var users = new List<User>
            {
                new User() { UserName="givecase", Password="123", Role = db.Roles.Single(r => r.RoleName == "管理员")},
                new User() { UserName="user01", Password="abc", Role = db.Roles.Single(r => r.RoleName == "会员")},
                new User() { UserName="user02", Password="123456", Role = db.Roles.Single(r => r.RoleName == "会员")},
            };
            users.ForEach(list => db.Users.AddOrUpdate(u => u.UserName, list));
            db.SaveChanges();


            var products = new List<Product>
            {
                new Product() { ProductName="联想电脑" },
                new Product() { ProductName="戴尔电脑" },
                new Product() { ProductName="三星手机" },
                new Product() { ProductName="苹果手机" },
                new Product() { ProductName="苹果电脑" }
            };
            products.ForEach(list => db.Products.AddOrUpdate(p => p.ProductName, list));
            db.SaveChanges();

            var categories = new List<Category>
            {
                new Category() {CategoryName="台式电脑",Products=new List<Product>() },
                new Category() {CategoryName="笔记本电脑",Products=new List<Product>() },
                new Category() {CategoryName="平板电脑",Products=new List<Product>() },

                new Category() {CategoryName="4G手机",Products=new List<Product>() },
                new Category() {CategoryName="3G手机",Products=new List<Product>() },
            };
            categories.ForEach(list => db.Categories.AddOrUpdate(c => c.CategoryName, list));
            db.SaveChanges();

            AddOrUpdateProduct(db, "台式电脑", "联想电脑");
            AddOrUpdateProduct(db, "台式电脑", "戴尔电脑");

            AddOrUpdateProduct(db, "平板电脑", "苹果电脑");

            AddOrUpdateProduct(db, "4G手机", "苹果手机");
            AddOrUpdateProduct(db, "4G手机", "三星手机");

            AddOrUpdateProduct(db, "3G手机", "三星手机");
            db.SaveChanges();
        }

        void AddOrUpdateProduct(EFContext context, string categoryName, string productName)
        {
            var cat = context.Categories.SingleOrDefault(c => c.CategoryName == categoryName);
            var pro = cat.Products.SingleOrDefault(p => p.ProductName == productName);
            if (pro == null)
            {
                cat.Products.Add(context.Products.Single(p => p.ProductName == productName));
            }
        }
    }
}

添加迁移

更新数据库

OK!没有出错。

查看表关系图:

注: 多对多 关系,创建一个中间表CategoryProduct, 查看其数据 :

8.小结

预留问题: 以上迁移有什么不足之处? 显然我们有时希望不用在 nuget 控制台 来执行,让程序在访问数据库时自动完成迁移。

方式:一是在程序代码中执行方法,可它也有一个缺点,那就是是否开启迁移,不是用 App.config 配置的;第二种方式,EF6可以在配置文件中来决定是否迁移.

本章重点是 迁移 初步认知,其次体会 多对多 关系实体设计(个人觉得这种关系比较扯淡)!

(注:对于留言的内容是交流,询问,反馈,我尽量回复。对随意而感的话题,抱歉我不回复!真心期待大家多多支持我!)