ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

.NET005-EF Core-1

2022-01-14 14:32:42  阅读:135  来源: 互联网

标签:Core 数据库 EF ctx NET005 optionsBuilder var new public


.NET005-EF Core-1

文档概述

关于EF Core的专题部分目前暂定四篇文章和一个项目(非新手项目),第一篇主要介绍ORM以及EF Core的简单使用和一些工具的使用以及EF Core的增删改查等等;第二篇介绍EF Core一对一、一对多、多对多关系配置;第三篇文章EF Core的优化、乐观并发控制、悲观并发控制等;第四篇文章表达式目录树动态构造。项目使用的数据库主要为SqlServer,但不仅限于此,也会用MySQL、PostgreSql等(主要为演示在不同数据库下EF Core如果转换为AST的差异性)。

本文主要介绍如下几个技术及实现:

  • 什么是ORM?
  • 如何搭建EF Core开发环境
  • 数据库迁移工具Migration如何使用及注意事项
  • 使用EF Core操作数据库(增、删、改、查、批量操作)
  • EF Core反向工程
  • 如何通过代码形式查看C#-->SQL语句以及三种主流数据库的实际情景

什么是ORM?

ORM(Object-Relational Mapping),中文翻译为对象关系映射,是为了解决面向对象和关系数据库匹配的技术,可简单理解为对象和数据库自动关联,修改对象的一些属性等等可同步到数据库中(映射元数据)。
在.NET中常见的ORM框架有如下:

  • EF Core 微软官方,要花较大精力去研究,否则容易出现效率低下等一些列问题 EF Core官方文档
  • Dapper
  • SqlSugar 轻量级框架,个人开发者的优秀框架,适合快速上手 sqlssugar官方文档
  • FreeSql

如何搭建EF Core开发环境

https://cdn.nlark.com/yuque/0/2021/png/957395/1632414095683-d969bd51-0c31-4111-aafb-0cb2850a9367.png

  • 创建实体类,例如:
 public partial class Person
    {
        public long Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        public string BirthPlace { get; set; }
        public double? Salary { get; set; }
        //?表示可空类型
        public DateTime? BirthDay { get; set; }
    }
 public partial class Dog
    {
        public long Id { get; set; }
        public string Name { get; set; }
    }
  • 创建配置类,继承IEntityTypeConfiguration,可在Configure方法中对属性(数据库中的字段)进行配置,例如下方将表名设置为T_Person
public class PersonConfig : IEntityTypeConfiguration<Person>
    {
        public void Configure(EntityTypeBuilder<Person> builder)
        {
            builder.ToTable("T_Person");
        }
    }
  • 创建继承自DbContext的自己的Context类,例如MyDbContext:DbContext,OnConfiguring()方法,可以配置连接数据库的类型及连接数据库的名称,也可以添加日志的输出(标准日志和简单日志)。OnModelCreating()方法则负责检索是加载当前程序集下面的所有IEntityTypeConfiguration还是其他程序级下面的实体类配置,也可以直接在这边进行实体类属性的约定配置。
 public class MyDbContext:DbContext
    {
        public DbSet<Book> Books { get; set; }
        public DbSet<Person> Persons {  get; set; }
        public DbSet<Dog> Dogs { get;set;  }
        public DbSet<Cat> Cats {  get; set; }
        public DbSet<Rabbit> Rabbits { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            string connStr = "Server = .; Database = AlbertBook; Trusted_Connection = True;MultipleActiveResultSets=true";
            //使用SqlServer连接数据库
            optionsBuilder.UseSqlServer(connStr);
            //支持批量删除和操作数据库
            optionsBuilder.UseBatchEF_MSSQL();
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            //从当前程序集加载所有的IEntityTypeConfiguration<T>
            modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
        }
    }

 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                //If you wanna convert the C# code to SQLServer, you should use the following package.
                //EFProvider-SQLServer PackageReference-Microsoft.EntityFrameworkCore.SqlServer
                //optionsBuilder.UseSqlServer("Server = .; Database = AlbertTemp; Trusted_Connection = True;MultipleActiveResultSets=true");

                //If you wanna convert the C# code to MySQL, you should use the following package.
                //EFProvider-MySql PackageReference-Pomelo.EntityFrameworkCore.MySql
                // Replace with your connection string.
                var connectionString = "server=localhost;user=root;password=eason12138.;database=AlbertTBooks";
                var serverVersion = new MySqlServerVersion(new Version(8, 0, 27));

                optionsBuilder.UseMySql(connectionString, serverVersion);


                //标准日志 Package-Microsoft.Extensions.Logging
                //optionsBuilder.UseLoggerFactory(loggerFactory);
                optionsBuilder.LogTo(msg =>
                {
                    if(!msg.Contains("Executing DbCommand")){ return; }
                    //msg是ef输出的消息
                    Console.WriteLine(msg);
                });
            }
        }

 protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.HasAnnotation("Relational:Collation", "Chinese_PRC_CI_AS");

            modelBuilder.Entity<TBook>(entity =>
            {
                entity.ToTable("T_Books");

                entity.Property(e => e.AuthorName)
                    .IsRequired()
                    .HasMaxLength(20)
                    .HasDefaultValueSql("(N'')");

                entity.Property(e => e.Title)
                    .IsRequired()
                    .HasMaxLength(100);
            });

            modelBuilder.Entity<TCat>(entity =>
            {
                entity.ToTable("T_Cats");

                entity.Property(e => e.Name)
                    .IsRequired()
                    .HasMaxLength(20);
            });

            modelBuilder.Entity<TPerson>(entity =>
            {
                entity.ToTable("T_Person");
            });

            modelBuilder.Entity<TRabbit>(entity =>
            {
                entity.ToTable("T_Rabbit");

                entity.Property(e => e.Id).ValueGeneratedNever();

                entity.Property(e => e.Name)
                    .IsRequired()
                    .HasMaxLength(20);
            });

            OnModelCreatingPartial(modelBuilder);
        }

数据库迁移工具Migration如何使用及注意事项

Migration数据库迁移,面向对象的ORM开发中,数据库不是程序员手动创建的,而是由Migration工具生成的。关系数据库库只是盛放模型数据的一个媒介而已,理想状态下,程序员无需关心数据库的操作。而根据对象定义的变化,自动更新数据库中的表以及表结构的操作,叫做Migration(迁移),迁移可以分为项目进化、项目回滚等。EF Core6.0 Migration官方文档

  • 安装NugetPackage(可以通过很多方式,具体请查阅nuget.org,笔者喜欢采用编辑*.proj):Microsoft.EntityFrameworkCore.Tools
  • 当实体类和实体配置类,以及继承的DbContext的MyDbContext都Ok后直接在Package Manager Console中输入Add-Migration InitialCreate(这是注释,类似于git commit -m "注释"),执行完毕后可自动生成C#代码,默认在Migrations文件夹下,可通过指定OutputDir来指定目录名称。
  • 更新提交到数据库,让数据库执行创表等系列操作。Update-database.
  • 回滚到指定版本号:Update-Database "版本号名称,就是上面的注释" 这边务必加上引号
  • 向上回滚一个版本直接执行 Remove-migration
  • 生成Sql执行脚本(适用于生产环境) Script-Migration D F 生成从D到F的Sql脚本语句

使用EF Core操作数据库(增、删、改、查、批量操作)

  1. 增删改查:直接new MyDbContext对象即可,通过Linq操作即可
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace _210917_Demon01_EFCoreAlbert
{
    internal class Program
    {
        /// <summary>
        /// <see cref="InitDataBase(DbContext)"/>
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        static async Task Main(string[] args)
        {
            //插入数据 ctx=逻辑上的数据库
            using (var ctx = new MyDbContext())
            {             
                var booksTable = ctx.Books;
                foreach (var item in booksTable)
                {
                    ctx.Remove(item);
                }               
                await ctx.SaveChangesAsync();
                //初始化数据表
                await InitDataBase(ctx);

                //查询
                var books = ctx.Books.Where(e => e.Price > 80);
                foreach (var item in books)
                {
                    Console.WriteLine(item.Title);
                }

                //查询是否存在一本叫Simple algorithm的书籍,如果存在则打印出作者名字
                var book = ctx.Books.Single(e => e.Title == "Simple algorithm");
                Console.WriteLine(book.AuthorName);

                books = ctx.Books.OrderBy(e => e.Price);
                foreach (var item in books)
                {
                    Console.WriteLine(item.Title);
                }

                //通过分组来取每一个作者的书数量和最大价格
                var groups = ctx.Books.GroupBy(e => e.AuthorName).Select(g => new
                {
                    Name = g.Key,
                    BooksCount = g.Count(),
                    MaxPrice = g.Max(e => e.Price)
                });
                foreach (var item in groups)
                {
                    Console.WriteLine($"Name:{item.Name}==" +
                        $"BooksCount:{item.BooksCount}==" +
                        $"MaxPrice:{item.MaxPrice}.");
                }
                //修改数据,albert作者的书籍的价格调高
                var albertBooks = ctx.Books.Where(e => e.AuthorName == "AlbertZhao");
                foreach (var item in albertBooks)
                {
                    item.Price = 198;
                }
                //删除书籍Top of the ware
                var cBook = ctx.Books.Single(e => e.Title == "Top of the ware");
                ctx.Remove(cBook);

                await ctx.SaveChangesAsync();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <remarks>Init DataBase</remarks>
        /// <param name="ctx"></param>
        /// <returns></returns>
        static async Task InitDataBase(DbContext ctx)
        {
            Book b1 = new Book()
            {
                AuthorName = "AlbertZhao",
                Title = "Simple algorithm",
                Price = 99,
                PubTime = new DateTime(2022, 12, 1)
            };
            Book b2 = new Book()
            {
                AuthorName = "ZackYang",
                Title = "Zero-Based fun learning C",
                Price = 59.8,
                PubTime = new DateTime(2019, 3, 1)
            };
            Book b3 = new Book()
            {
                AuthorName = "WuJun",
                Title = "The beauty of math",
                Price = 99,
                PubTime = new DateTime(2018, 1, 1)
            };
            Book b4 = new Book()
            {
                AuthorName = "WuJun",
                Title = "Top of the ware",
                Price = 198,
                PubTime = new DateTime(2021, 1, 1)
            };
            Book b5 = new Book()
            {
                AuthorName = "Liangtongming",
                Title = "In-depth upderstanding of asp.net core",
                Price = 169,
                PubTime = new DateTime(2021, 1, 1)
            };

            //将对象数据添加到内存逻辑的数据表中
            await ctx.AddAsync(b1);
            await ctx.AddAsync(b2);
            await ctx.AddAsync(b3);
            await ctx.AddAsync(b4);
            await ctx.AddAsync(b5);

            //将内存中的数据同步到数据库里
            await ctx.SaveChangesAsync();
        }
    }
}
  1. 批量操作(Zack.EFCore.Batch.MSSQL开源包)
    说明使用文档:Zack.EFCore.Bathc.MSSQL使用文档
MyDbContext :DbContext
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            string connStr = "Server = .; Database = AlbertBook; Trusted_Connection = True;MultipleActiveResultSets=true";
            optionsBuilder.UseSqlServer(connStr);
            optionsBuilder.UseBatchEF_MSSQL();
        }

Main()
//使用Zack.EFCore.Batch
                await ctx.DeleteRangeAsync<Book>(e => e.Price > 80 && e.AuthorName == "WuJun");
                await ctx.BatchUpdate<Book>()
     .Set(b => b.Price, b => b.Price + 3)
     .Set(b => b.Title, b => "HelloWorld")
     .Set(b => b.AuthorName, b => b.Title.Substring(3, 2) + b.AuthorName.ToUpper())
     .Set(b => b.PubTime, b => DateTime.Now)
     .Where(b => b.Id > 1 || b.AuthorName.StartsWith("Albert"))
     .ExecuteAsync();
//批量插入数据,一个list直接搞定
List<Book> books = new List<Book>();
for (int i = 0; i < 100; i++)
{
	books.Add(new Book { AuthorName = "abc" + i, Price = new Random().NextDouble(), PubTime = DateTime.Now, Title = Guid.NewGuid().ToString() });
}
using (TestDbContext ctx = new TestDbContext())
{
	ctx.BulkInsert(books);
}

EF Core反向工程

从数据库生成实体类--不推荐使用这种方法,直接使用第三方工具。三种建模方式:DBFirst数据库优先(数据库先建好)、ModelFirst模型优先(图形化先建好)、CodeFirst代码优先。已经存在表了,想利用反向工程将存在的表反向生成。

  • 新建项目,将引用的包拷贝到新项目中,设置启动项目为当前项目。
<ItemGroup>
	  <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.11" />
	  <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.11">
	    <PrivateAssets>all</PrivateAssets>
	    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
	  </PackageReference>
</ItemGroup>
  • 执行以下命令:Scaffold-DbContext 数据库连接字符串 数据库类型
Scaffold-DbContext "Server = .; Database = AlbertBook; Trusted_Connection = True;MultipleActiveResultSets=true" Microsoft.EntityFrameworkCore.SqlServer
    1. 如果新建了一个表,需要强制覆盖,在最后加上-force
Scaffold-DbContext "Server = .; Database = AlbertBook; Trusted_Connection = True;MultipleActiveResultSets=true" Microsoft.EntityFrameworkCore.SqlServer -Force

如何通过代码形式查看C#-->SQL语句以及三种主流数据库的实际情景

  1. 标准日志
    optionBuilder.UseLoggerFactory(ILoggerFactory),下方没有通过依赖注入形式,直接使用了静态对象。
//在DbContext继承类中输入以下代码
private static readonly ILoggerFactory loggerFactory = LoggerFactory.Create(builder=>builder.AddConsole());

 protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                optionsBuilder.UseSqlServer("Server = .; Database = AlbertBook; Trusted_Connection = True;MultipleActiveResultSets=true");
                optionsBuilder.UseLoggerFactory(loggerFactory);
            }
        }
//在Program.cs中输入以下代码
 static void Main(string[] args)
        {              
            using (var ctx = new AlbertBookContext())
            {
                var books = ctx.TBooks.OrderBy(e => e.Price);
                foreach (var item in books)
                {
                    System.Console.WriteLine(item.Title);
                }
            }
        }
  1. 简单日志
    可以通过if来判断msg,来获取想要的信息。msg是从连接数据库、创建数据库到执行语句以及最后的关闭连接所有的信息。
optionsBuilder.LogTo(msg =>
                {
                    //msg是ef输出的消息
                    Console.WriteLine(msg);
                });
  1. ToQueryString
    使用Package:Microsoft.EntityFrameworkCore,无需执行即可拿到,对象.ToQueryString()方便开发者查看。
//We need execute var ctx = new AlbertBookContext:DbContext 
//Console.WriteLine(books.ToQueryString())
static void Main(string[] args)
    {              
       using (var ctx = new AlbertBookContext())
       {
                var books = ctx.TBooks.OrderBy(e => e.Price);
                foreach (var item in books)
                {
                    System.Console.WriteLine(item.Title);
                }
                string sqlServerOrderByPrice = books.ToQueryString();
                System.Console.WriteLine(sqlServerOrderByPrice);
     }
}

附录

  1. SQLServer中的Sql Server Profiler可以查询所有数据库接收到的Sql语句,在Tools中
  2. 关于数据库主键配置,是选择自增还是GUID还是复合主键请自行查阅研究,目前互联网主流使用雪花算法、Hi\Lo算法、混合主键等
  3. 注意有些C#语句是EFCore无法转换的,比如将查询一个name是否contains xxx,如果你写成如下形式,是没办法被翻译过去的:
 var books = ctx.Books.Where(b=>IsOk(b.Title));
 private static bool IsOk(string s){
     return s.Contains("Albert");
 }
 //Notes:这边有个小知识,顺便提一下
 //LINQ中的Where实际就是一个委托,来判断条件是否满足,在满足的适合直接使用yield关键字,不会一直查询,通过迭代器来实现提前返回。

标签:Core,数据库,EF,ctx,NET005,optionsBuilder,var,new,public
来源: https://www.cnblogs.com/hongyongzhao/p/15801742.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有