首页 > 技术文章 > .net core 中使用 EFcore做ORM

rush-peng 2020-10-22 21:11 原文


1.准备

  1. 安装mysql数据库

  2. 如果使用EF core,查阅efcore 官方文档https://docs.microsoft.com/zh-cn/ef/core/get-started/?tabs=visual-studio

  3. EF core 的各种提供商https://docs.microsoft.com/zh-cn/ef/core/providers/?tabs=dotnet-core-cli

使用 Mysql的源码https://gitee.com/rush_peng/netcore-31---medium-ef-core.git源码的版本要对应,不然容易报错



使用mysql 数据库进行操作,只要装两个包就行
在这里插入图片描述




2.安装相应的 nuge 包

Install-Package MySql.Data.EntityFrameworkCore -Version 8.0.22

EF core 的 PMC 工具

Install-Package Microsoft.EntityFrameworkCore.Tools
Add-Migration InitialCreate  #命令为迁移搭建基架
Update-Database              #命令创建数据库并向其应用新的迁移

验证是否安装成功

Get-Help about_EntityFrameworkCore
_/\__
               ---==/    \\
         ___  ___   |.    \|\
        | __|| __|  |  )   \\\
        | _| | _|   \_/ |  //|\\
        |___||_|       /   \\\/\\

TOPIC
    about_EntityFrameworkCore

SHORT DESCRIPTION
    Provides information about the Entity Framework Core Package Manager Console Tools.

<A list of available commands follows, omitted here.>



3.软件包管理控制台

在实际项目中,数据模型随着功能的实现而变化:添加和删除新的实体或属性,并且需要相应地更改数据库架构,使其与应用程序保持同步。 EF Core 中的迁移功能能够以递增方式更新数据库架构,使其与应用程序的数据模型保持同步,同时保留数据库中的现有数据。



3.1迁移的过程

1.比如你刚开始创建了一个程序

public class Blog
{
    public int Id { get; set; }
    public string Name { get; set; }
}

2.这个时候,你需要做一个迁移:(数据库字段从无到有)

Add-Migration InitialCreate

EF Core 将在项目中创建一个名为“Migrations”的目录,并生成一些文件。 最好检查 EF Core 生成的内容,并在可能的情况下修改它,但现在我们跳过此操作。 initialCreate 是自己给此次迁移起的一个名字。

3.执行迁移

Update-Database

4.过了几天,你又修改了模型

public class Blog
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime CreatedTimestamp { get; set; }
}

5.执行迁移命令

Add-Migration AddBlogCreatedTimestamp
Update-Database


3.2其他的 EF Core 命令行工具

若要显示有关命令的帮助信息,请使用 PowerShell Get-Help 命令。

  • Add-Migration : 添加新的迁移
  • Drop-Database : 删除数据库
  • Remove-Migration:删除上一次迁移 (回滚对迁移) 所做的代码更改。
  • Update-Database:将数据库更新到上次迁移或指定迁移。



4.一个简单的应用实例

一个学校的数据库表,一个班级对应多个学生,一个学生,对应一个班级


4.1建表

DbsetClassrooms 一定要加 pubulic

 public class School:DbContext
    {
        public DbSet<Classroom> Classrooms { get; set; } //Classrooms 是表名字
        public DbSet<Student> Students { get; set; }     //Students 是表名字
        protected override void OnConfiguring(DbContextOptionsBuilder options)
 => options.UseMySQL
 ("server=localhost;database=studentmessage;user=root;password=123456");
//连接本地数据库
// options.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=test02;Trusted_Connection=True;MultipleActiveResultSets=true");
      
    }

    public class Classroom
    {
        public int ClassroomId { get; set; } //带有id 的默认就是主键
        public string address { get; set; }
        public List<Student> Students { get; set; } = new List<Student>();  //导航属性,实际的数据库中并没有

    }
    public class Student
    {
        public int StudentId { get; set; }
        public string Gender { get; set; }
        public int ClassroomId { get; set; }
        public Classroom classroom { get; set; }  //导航属性,实际的数据库中并没有
    }

4.2 增删改查操作

           using (var db = new SchoolDbContext())
            {
                //create 
                Console.WriteLine("添加一个班级");
                db.Add(new Classroom { address = "教学楼一楼", ClassroomId = 4 }); //注意 相同的 add 命令,只能执行一次。
                db.Add(new Classroom { address = "教学楼二楼", ClassroomId = 3 });
                db.SaveChanges();

                //read
                Console.WriteLine("读一个班级的信息");
                var classroomdb01 = db.Classrooms
                    .OrderBy(b => b.ClassroomId)
                    .First();
                Console.WriteLine(classroomdb01.ClassroomId);

                //update
                //更新的是上边选出来的人的
                classroomdb01.address = "教学楼楼顶";
                db.SaveChanges();
                classroomdb01.Students.Add(
                    new Student { Name = "张三", Gender = "男", StudentId = 1, ClassroomId = 1 }
                    );
                db.SaveChanges();

                //delete
                db.Remove(classroomdb01);
                db.SaveChanges();
            }


5.指定表之间的关系

参考文献1对这块写的不错,可以看看
目前 EF 不能直接实现多对多的关系,需要用个中间类做调整。两边的导航属性是数组,中间类的两个导航属性是实体(可以看看下边 6复杂点的例子)

  • 声明一个主体实体(主表)与依赖实体(从表)
  • 指定modelBuilder.Entity,T是依赖实体或者主体实体
  • HasOne 或 HasMany 标识依赖实体中(即正在配置的)的导航属性
  • WithOne 或 WithMany 来标识反向实体的导航属性
  • HasForeignKey中标识外键(从表)

看下边的例子:

配置多对一的关系,可以使用两种写法,这里City是依赖实体(从表),其中包含外键ProvinceId,Province是主体实体

 //第一种 modelBuilder.Entity<依赖主体>
modelBuilder.Entity<City>().HasOne(x => x.Province).
WithMany(x => x.Cities).HasForeignKey(x => x.ProvinceId);
 
//第二种  modelBuilder.Entity<实体主体>
modelBuilder.Entity<Province>().HasMany(x => x.Cities).
WithOne(x => x.Province).HasForeignKey(x => x.ProvinceId);


6.一个复杂点的例子

  • 创建数据库上下文
  • 上下文依赖注入使用
  • 添加种子数据
  • 创建表的时候指定表明

前期准备,建立学生 . 课程. 选课 三个类!

 public class Course
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int CourseID { get; set; }
        public string Title { get; set; }
        public int Credits { get; set; }
        public ICollection<Enrollment> Enrollments { get; set; }
    }
 public class Student
    {
        public int ID { get; set; }
        public string LastName { get; set; }
        public string FirstMidName { get; set; }
        public DateTime EnrollmentDate { get; set; }
        public ICollection<Enrollment> Enrollments { get; set; }
    }
public enum Grade
    {
        A, B, C, D, F
    }
    public class Enrollment
    {
        public int EnrollmentID { get; set; }
        public int CourseID { get; set; }
        public int StudentID { get; set; }
        public Grade? Grade { get; set; }
        public Course Course { get; set; }
        public Student Student { get; set; }
    }

1.创建数据库上下文

 public class SchoolContext : DbContext
    {
        public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
        {
        }
        public DbSet<Course> Courses { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Student> Students { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Course>().ToTable("TableCourse"); //指定表的名字
            modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
            modelBuilder.Entity<Student>().ToTable("Student");

            //添加种子数据
            modelBuilder.Entity<Student>().HasData(
                        new Student {ID=1, FirstMidName = "Carson", LastName = "Alexander", EnrollmentDate = DateTime.Parse("2005-09-01") }
                       ); }
    }

2.依赖注入使用上下文:

 services.AddDbContext<SchoolContext>(options =>
       options.UseMySQL(Configuration.GetConnectionString("DefaultConnection")));
       // GetConnectionString 按 F12 进去,他就是找一个名叫 ConnectionStrings 的字符串

3.在appsettings.json 文件里添加链接字符串

 "ConnectionStrings": {
     "mysqlConnection": "server=localhost;database=progress_track;user=root;password=123456",
    "aliyunMySql": "server=rm.aliyuncs.com;port=3306;database=mysql;user=zhao;password=xxxxxxx",
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=progress_track;Trusted_Connection=True;MultipleActiveResultSets=true"
  },

4.在需要用到数据库的地方,直接依赖注入引用就可以
注意一下异步的用法

  public class StudentController : ControllerBase
    {
        private readonly SchoolContext schoolContext;
        private int dex = 2;

        public StudentController(SchoolContext schoolContext)
        {
            this.schoolContext = schoolContext;
        }
        [HttpGet("add")]
        public async Task<int> AddCreateAsync() //只要里面有异步的思想,就要使用 Task
        {
            var students = new Student[]
               {
            new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2005-09-01")},
            new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")},
            new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")}
               };

            foreach (var student in students)
            {
                student.ID = ++dex;
                schoolContext.Students.Add(student);
            }
            var resut = await schoolContext.SaveChangesAsync();
            return resut;
        }
    }

7.忽略某一个属性

如何在 Entity 中有,但是不在数据库中建立相应的字段

modelBuilder.Entity<Company>().Ignore(x=>x.testIgnore)


8.多对多

以city和company为例,一个city会有多个company,一个company在多个city都有多个分company

using System;
using Microsoft.EntityFrameworkCore;
 
namespace ASPNetEFFCore.Models
{
    public class MyContext:DbContext
    {
        public MyContext(DbContextOptions<MyContext> options):base(options)
        {
        }
 
       protected override void OnModelCreating(ModelBuilder modelBuilder){
           //使用x.CityId,x.CompanyId生成 cityCompany的联合主键
           //执行数据库迁移
           modelBuilder.Entity<CityCompany>().HasKey(x => new{x.CityId,x.CompanyId});
 
            //配置多对多 ,就是两个一对多,可以不写
           modelBuilder.Entity<CityCompany>().HasOne(x => x.City).WithMany(x=> x.CityCompany).HasForeignKey(x=>x.CityId);
         modelBuilder.Entity<CityCompany>().HasOne(x => x.Company).WithMany(x=> x.CityCompany).HasForeignKey(x=>x.CompanyId);
    
        }
  
        public DbSet<City> Cities { get; set; }
 
        public DbSet<CityCompany> cityCompanies {get;set;}
    }
    
    public class City
    {
         public City()
        {
            CityCompany = new List<CityCompany>();
        }
 
        public int Id { get; set; }
        public string Name { get; set; }
        public string AreaCode { get; set; }
        public int ProvinceId { get; set; }
        public Province Province { get; set; }
 
        //多对多映射
        public List<CityCompany> CityCompany {get;set;}
    }
 
     public class Company
    {
        public Company()
        {
            CityCompany = new List<CityCompany>();
        }
 
        public int Id {get;set;}
        public string Name {get;set;}
        public DateTime EstablishDate {get;set;}
        public string LegalPerson {get;set;}
 
        //多对多映射
        public List<CityCompany> CityCompany {get;set;}
    }
    //多对多中间model
    public class CityCompany
    {
       public int CityId {get;set;}
       public City City{get;set;}
       public int CompanyId {get;set;} 
       public Company Company {get;set;}
    }
}

参考文献:

[1]https://blog.csdn.net/weixin_41372626/article/details/104740176
[2]微软官方网站

推荐阅读