• 初识EFCore


    一、引言

    最近在用MVVM模式开发一个项目,UI和业务逻辑部分写的差不多了,两部分对应到MVVM就是View和ViewModel(在三层架构中可以认为是UI呈现层和BLL业务逻辑层)。接着开始写数据访问,但数据访问部分,我没系统地研究过该怎么写,之前都是直接写在VM中和业务代码交织在一起的,去网上查询了一番之后,发现在三层架构中往往会将其写在DAL层(所以很多开源项目就项目目录下会有UI、BLL、DAL三个文件夹),在MVVM模式中会写在Model中(也有写在VM中的,但是从我对MVVM的认识来看,VM是处理业务逻辑的,数据访问不应该是这部分的代码,当然有些数据模型是从底层数据抽象出来的也可以放在这一层,见仁见智)。但不管怎么说,这次实现数据访问我决定换掉这种原始的公开一个数据访问连接的方式(在类内定义一个SqlConnect字段)。

    于是我开始在MVVM中模仿三层架构DAL层的写法。不过学习过程中,发现别人都不是像我一样用原始的SQL语句去访问的。心想确实,我的数据库访问技术已经落后别人几个世代了。我平时很少使用数据库,即使用到也只是简单地增删改查(CRUD),通常几条简单SQL语句就能满足,根本不需要关注其他访问技术。正好这次有这个机会,而且还是在用.NET下的东西开发,那何不试试呢?

    二、学习

    之前学习.NET时,有了解到.NET提供了对象关系映射器(ORM)和一种在代码中写SQL查询的方式。是叫做Entity Framework Core(实体框架核心,简称EFCore)和LINQ(语言继承查询)的技术。正好刚刚百度时看到,很多人也都是使用这两者来实现数据访问的,LINQ我有一定了解,所以这里着重学习下EFCore。

    2.1. EFCore概述

    当你使用关系数据库如SQL Server、Oracle或PostgreSQL时(MySQL也是可以的,只是微软旧的网页上没写),推荐使用EF来实现持久层(也称DAL,数据访问层)。EF(Entity Framework是EFCore的前身,命名上有.NET Core和.NET Framework的感觉)支持LINQ并为你模型提供强类型对象,并简化你数据库中的持久性。

    EF作为.NET Framework的一部分已有一段历史了。当你使用.NET时,你也应该使用EFCore,它能与.NET相同的方式运行在Windows或Linux上。

    上面说的.NET应该是指.NET Core3.1以及之后的版本(这个是.NET命名的历史原因)。
    嫌复杂就可以简单地认为.NET是最新叫法,.NET Core是.NET Framework的较新实现。三者其实是一个东西。
    EFCore最好配.NETCore使用,两者都在旧的.NET Framework、EF的基础上实现了跨平台,且功能更强大了。
    
    • 1
    • 2
    • 3

    EFCore是EF一次完全的重写,它的实现占用了更少的内存控件,并且提升了性能。

    EFCore是曾经流行的EF数据访问技术的一个轻量级、可扩展且跨平台的版本。它是在2016年年中和.NETCore一起引入的。

    EF,Entity Framework,说说我对这个叫法的理解。一般操作数据的话,就是对用SQL语句对数据库对象进行操作,
    这是比较抽象的,就感觉你操作的是存储在数据库中的数据。而EF是以Entity Class为核心的,也叫实体类,你操作
    的是该类的对象了,从某种程度上来讲你操作的变成一个有数据体了。
    而以数据体为核心的这一套数据访问框架,称为实体框架。至于后来的EFCore,纯粹是为了与.NET Core配对吧。
    
    • 1
    • 2
    • 3
    • 4

    2.2. model

    使用EFCore时,数据访问是使用model来进行的。model是由实体类和代表与数据库会话的上下文组成的。上下文对象允许查询和保存数据。

    model这个东西,可以理解为数据模型,而且是根据面向对象思想从现实中抽象出来的模型,在代码层面来说,它比纯过程纯逻辑的代码更易于人理解。
    如果以面向对象思想直接从现实世界中抽象出模型,那这个模型在代码层面通常是类的形式,而代码层面的一个类显然是不能
    直接访问数据库的。所以上面写了EFCore的完整model应该是由实体类(其实就是面向对象的抽象类)和数据会话的上下文组成的。
    那么问题来了,啥是数据会话的上下文呢。这个得往下看代码,现在只知道上下文可以结合实体类来访问数据库的。那它肯定有自己一套访问数据库的机制。
    
    • 1
    • 2
    • 3
    • 4

    说了这么多废话,看下面一组图轻松一下。

    如果说下图是使用SQL直接操作数据库。想象一下你敲着SQL语句,来CRUD数据库中各种各样的表。对于不熟悉数据库的人来说,还挺头疼的。或者说,即使你的SQL语句达到了效果,这些查询语句只能与最终查询结果匹配,很难说你操作的对象是个啥。
    在这里插入图片描述
    我想使用了EF后,情况就变成了一下这样:
    在这里插入图片描述
    在数据和程序之间出现了一个模型,emmm,当然在大部分程序中在不会出现那么形象的三维模型,来让你对其调参来修改数据,而是代码层面上的一个抽象类型。
    那么在看模型代码之前,先了解一下几种方式,
    EF支持下列的模型开发方式:

    • 从已有的数据库中生成model
    • 手工来编写一个匹配数据库的model
    • 一旦创建了一个模型,使用EF Migrations来根据模型创建一个数据库。Migrations允许在模型更改时同时使数据库发生变化。

    显然三种方式有各自的特点,对于我这样的初学者来说,感觉应该先尝试第二种。因为项目数据库已经建好,通过手写一个model,更能体会其中的一些机理。

    2.2.1. 创建并配置一个模型

    EFCore使用一系列约定来构建基于实体类特征的模型。你可以指定额外的配置来补充或重写约定的内容。

    2.2.1.1. 用fluent API来配置一个模型

    你能在你派生的上下文中重写OnModelCreating方法,并使用ModelBuilder API来配置你的模型。这是最有效的配置方法,它允许你在不修改实体类的情况下指定配置。fluent API配置有着最高的优先级,并会覆盖约定和数据标注。

    using Microsoft.EntityFrameworkCore;
    
    namespace EFModeling.EntityProperties.FluentAPI.Required;
    
    internal class MyContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
    
        #region Required
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Blog>()
                .Property(b => b.Url)
                .IsRequired();
        }
        #endregion
    }
    
    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    按组配置
    为了减小OnModelCreating方法的大小(其实就是让里面看起来代码少一点),实体类型的所有配置项可以提取到一个实现了IEntityTypeConfiguration<TEntity>接口的单独的类中。

    public class BlogEntityTypeConfiguration : IEntityTypeConfiguration<Blog>
    {
        public void Configure(EntityTypeBuilder<Blog> builder)
        {
            builder
                .Property(b => b.Url)
                .IsRequired();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    然后从OnModelCreating中调用Configure方法,

    new BlogEntityTypeConfiguration().Configure(modelBuilder.Entity<Blog>());
    
    • 1

    也完全可以在在给定的程序集中申请IEntityTypeConfiguration指定的所有配置项,

    modelBuilder.ApplyConfigurationsFromAssembly(typeof(BlogEntityTypeConfiguration).Assembly);
    
    • 1
    注意:申请配置项的顺序是未定义的,因此该方法应仅在顺序无关时使用。
    
    • 1

    2.2.1.2. 使用数据标注来配置一个模型

    你还可以应用特性(也称为数据标注,数据注释,Data Annotations)到你的实体类和属性上。数据标注会覆盖约定,但会被Fluent API配置项给覆盖。

    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    using Microsoft.EntityFrameworkCore;
    
    namespace EFModeling.EntityProperties.DataAnnotations.Annotations;
    
    internal class MyContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
    }
    
    [Table("Blogs")]
    public class Blog
    {
        public int BlogId { get; set; }
    
        [Required]
        public string Url { get; set; }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    或许初学者(比如我),在看了上面两种创建&配置模型的方法后,对模型怎么连数据库,怎么与数据库通信还是没有一个清晰的认识。
    不过很正常,因为这小节内容本来就是讲怎么创建,而不是怎么访问。
    而且从以上两种方式还是可以看出一些东西的,实体类 = 面向对象的抽象模型(类型) 这点应该是没跑了。
    然后上下文中会应用这个实体类,来进行一些关联。像第二种数据注释的方式,甚至在实体类上直接出现了[Table("Blogs")]这种代码,
    很明显给人感觉是该实体类和数据表关联起来了。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.3. 查询

    使用LINQ来从数据库中检索实体类的实例,以实现查询。
    LINQ允许你使用C#(或其他.NET平台支持的语言)来写强类型查询。
    它使用你派生的上下文和实体类来引用数据库对象。
    EFCore将LINQ查询表示传递给数据库提供程序(DataBase Provider,数据库提供者其实就是数据库和你的应用程序之间的一个中间件,供你访问数据库用的)。数据库提供程序将其转换为特定数据库的查询语言(例如,关系数据库的SQL)。即使结果中返回的实体已经存在于上下文中,也会对数据库执行查询。

    下面代码展示了一些如何用EFCore来实现常见任务的示例:

    2.3.1. 加载所有数据

    using (var context = new BloggingContext())
    {
        var blogs = context.Blogs.ToList();
    }
    
    • 1
    • 2
    • 3
    • 4

    2.3.2. 加载单个实体

    using (var context = new BloggingContext())
    {
        var blog = context.Blogs
            .Single(b => b.BlogId == 1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.3.3. 过滤

    using (var context = new BloggingContext())
    {
        var blogs = context.Blogs
            .Where(b => b.Url.Contains("dotnet"))
            .ToList();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.3.4. 进一步理解&使用

    • 学习LINQ查询表达式,写起来会有更多花样
    • 关于EFCore查询的细节,请看How queries Work

    2.4. 保存数据

    使用实体类的实例来在数据库中创建、删除、修改数据。
    每个上下文实例都有一个ChangeTracker来负责追踪需要写入数据库的改变。当你对实体类的实例进行更改时,这些更改会记录在ChangeTracker中,然后在你调用SaveChanges时写入数据库。数据库提供程序(DataBase Provider)负责将这些更改转换为特定的数据库操作(例如,关系数据库的INSERT、UPDATE和DELETE命令)。

    using (var db = new BloggingContext())
    {
        var blog = new Blog { Url = "http://sample.com" };
        db.Blogs.Add(blog);
        db.SaveChanges();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.5. △EF O/RM注意事项

    尽管EFCore擅长抽象许多编程细节,但是这里有一些适用于任何O/RM的最好的实践,有助于避免一些生产应用中的常见错误:

    EFCore的方式确实屏蔽了许多细节,在使用的时候看起来就是操作一个抽象对象即可,很简便。但是呢,这边给出了一些
    经过实践得出的非常好的意见,希望开发者可以吸收消化。
    
    • 1
    • 2
    • 中级或更高层次的底层数据库服务器知识对于在高性能的生产应用程序中构建、调试、配置和迁移数据是至关重要的。例如,主键和外键、约束、索引、规范化、DML和DDL语句、数据类型、分析等知识。

    • 功能和集成测试:尽可能地模拟生产环境是非常重要的:

      • 在只有某些特定版本的数据库服务器下才会复现的问题
      • 在升级EFCore和其它依赖项时捕获异常。例如:添加或升级ASP .NET Core、OData或AutoMapper,这些依赖项会以意想不到的方式影响EF Core。
    • 性能与压力测试。

    • 安全性审查。

    • 确保日志记录和诊断是充分的和可用的。

    • 错误恢复。

    • 应用程序部署与迁移。

    • 生成迁移的详细检查与测试。

    三、结尾

    第一次接触EFCore这种OR/M技术,能理解几个主要概念的含义(模型、实体类、上下文)即可,更深入的知识点之后再研究。

  • 相关阅读:
    【外汇天眼】美国CFTC官方发布:外汇交易前你应该知道的八件事
    Jenkins+Gitee+Docker+Ruoyi项目前后端分离部署
    实时数据备份实践inotify和rsync联动
    【从头构筑C#知识体系】2.1 泛型
    CCE云原生混部场景下的测试案例
    谷歌最新版本下载最新驱动网址chrome driver Version: 122.0.6261.111
    关于python中有关一个子类继承多个父类的实例属性绑定问题
    PID控制原理
    java-php-net-python-论坛网站计算机毕业设计程序
    【PE806】Nim on Towers of Hanoi(汉诺塔游戏,生成函数)
  • 原文地址:https://blog.csdn.net/BadAyase/article/details/125444618