.NET-4.ORM 常见框架EFcorn、Dapper、SqlSugar、FreeSql 和ADO.NET


前言

学习参考:

1. .NET 6教程,.Net Core 2022视频教程,杨中科主讲--- 以及资料参考
2. 官方文档EF core
常用的语句
3..netmvc
https://www.cnblogs.com/1016391912pm/p/12024671.html
https://github.com/dotnet/EntityFramework.Docs/tree/main/samples/core/Querying/Overview
第一个EFCore应用
https://docs.microsoft.com/zh-cn/ef/core/modeling/entity-types?tabs=fluent-api

//添加包
Microsoft.EntityFrameworkCore.Sqlite
Pomelo.EntityFrameworkCore.MySql
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Tools
Add-Migration InitialCreate//实现迁移
Update-database//生成数据库
Update-Database InitialCreate
Remove-migration//删除最后一次的迁移脚本
Script-Migration//生成迁移SQL代码
Script-Migration D F//生成版本D到版本F的SQL脚本
Script-Migration D//生成版本D到最新版本的SQL脚本:

dotnet add package Microsoft.EntityFrameworkCore.Design//添加 EF Core 工具所需的包。
dotnet tool install --global dotnet-ef//将安装 dotnet ef,用于创建迁移和基架的工具。
dotnet ef migrations add InitialCreate --context PizzaContext//创建数据库的迁移
dotnet ef database update --context PizzaContext//将应用迁移生成数据库。

builder.Services.AddSqlite<PizzaContext>("Data Source=ContosoPizza.db");

什么是ORM

(Object Relational Mapping关系对象映射)
让开发者用对象操作的形式操作关系数据库。
.net 常用的orm:EF coreDapper、SqlSugar、FreeSql

EF Core学习与深入

一、了解 EF Core

1、Entity Framework Core(EF Core)是微软官方的ORM框架。优点:功能强大、官方支持、生产效率高、力求屏蔽底层数据库差异;缺点:复杂、上手门槛高、不熟悉EFCore的话可能会进坑。
2、Dapper。优点:简单,N分钟即可上手,行为可预期性强;缺点:生产效率低,需要处理底层数据库差异。
3、EF Core是模型驱动(Model-Driven)的开发思想,Dapper是数据库驱动(DataBase-Driven)的开发思想的。没有优劣,只有比较。
4、性能: Dapper等≠性能高;EF Core≠性能差。
5、EF Core是官方推荐、推进的框架,尽量屏蔽底层数据库差异,.NET开发者必须熟悉,根据的项目情况再决定用哪个。

对于后台系统、信息系统等和数据库相关开发工作量大的系统,且团队比较稳定,用EF Core;对于互联网系统等数据库相关工作量不大的系统,或者团队不稳定,用Dapper。

1.DbContext

EF中的上下文(DbContext)简介
DbContext是实体类和数据库之间的桥梁,DbContext主要负责与数据交互,主要作用:

1、DbContext包含所有的实体映射到数据库表的实体集(DbSet < TEntity >)。
2、DbContext 将LINQ-to-Entities查询转换为SQL查询并将其发到数据库。
3、更改跟踪: 它跟踪每个实体从数据库中查询出来后发生的修改变化。
4、持久化数据: 它也基于实体状态执行插入、更新和删除操作到数据库中

DbContext类是EntityFramework (简称 EF)中的一个类,可以理解为一个数据库对象的实例。在 EF中,无需手动的拼接 SQL 语句对数据库进行增删改查,而是通过 DbContext 来进行相应操作。

2.数据注释、Fluent API学习

https://docs.microsoft.com/zh-cn/ef/core/modeling/entity-types?tabs=fluent-api

1、视图与实体类映射:modelBuilder.Entity<Blog>().ToView("blogsView");
2、排除属性映射:
modelBuilder.Entity<Blog>().Ignore(b => b. Name2);
3、配置列名:
modelBuilder.Entity<Blog>().Property(b =>b.BlogId).HasColumnName("blog_id");
4、配置列数据类型:
builder.Property(e => e.Title) .HasColumnType("varchar(200)")
5、配置主键
默认把名字为Id或者“实体类型+Id“的属性作为主键,可以用HasKey()来配置其他属性作为主键。modelBuilder.Entity<Student>().HasKey(c => c.Number);
支持复合主键,但是不建议使用。
6、生成列的值
modelBuilder.Entity<Student>().Property(b => b.Number).ValueGeneratedOnAdd();
7、可以用HasDefaultValue()为属性设定默认值
modelBuilder.Entity<Student>().Property(b => b.Age).HasDefaultValue(6);
8、索引
modelBuilder.Entity<Blog>().HasIndex(b => b.Url);
复合索引
modelBuilder.Entity<Person>().HasIndex(p => new { p.FirstName, p.LastName });
唯一索引:IsUnique();聚集索引:IsClustered()
9...  用EF Core太多高级特性的时候谨慎,尽量不要和业务逻辑混合在一起,以免“不能自拔”。比如Ignore、Shadow、Table Splitting等……
## ef core
//模型声明Blogs、AuditEntry、Post
internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<AuditEntry>();
    }
}

# 从模型中排除类型
[NotMapped]
public class BlogMetadata
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    [NotMapped]//排除属性
    public DateTime LoadedFromDatabase { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Ignore<BlogMetadata>();
   // modelBuilder.Entity<BlogMetadata>()
   //     .Ignore(b => b.LoadedFromDatabase);
}

# 表名称、表架构、表注释
按照约定,每个实体类型都将设置为映射到与公开实体的 DbSet 属性名称相同的数据库表。 如果给定实体不存在 DbSet,则使用类名称。
[Table("blogs", Schema = "blogging")]
[Comment("Blogs managed on the website")]//表注释
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .ToTable("blogs");
   // modelBuilder.Entity<Blog>()
   //     .ToTable("blogs", schema: "blogging");
   modelBuilder.Entity<Blog>()
        .HasComment("Blogs managed on the website");
}
# 模型的数据注释和FluentAPI的属性定义
.HasKey(c => c.LicensePlate);[Key]//主键
.HasName("PrimaryKey_BlogId");//主键名
.Ignore(b => b.LoadedFromDatabase);[NotMapped]//排除类型

HasColumnName("blog_id");[Column("blog_id")]//列名
HasColumnType("varchar(200)");[Column(TypeName = "varchar(200)")]//列数据类型
HasMaxLength(500);[MaxLength(500)]//最大长度
HasPrecision(14, 2);[Precision(14, 2)]//属性配置为精度为 14 和小数位数为 2
IsUnicode(false);[Unicode(false)]//Unicode
IsRequired();[Required]//必须属性
.HasComment();[Comment("Blogs managed on the website")]//列注释
.HasColumnOrder(0);[Column(Order = 0)]//列顺序
.HasDefaultValue(3);//默认值
.HasDefaultValueSql("getdate()");//默认值

public class Blog
{
    [Column("blog_id")]
    [MaxLength(500)]
    public int BlogId { get; set; }

    [Column(TypeName = "varchar(200)")]
    public string Url { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.BlogId)
        .HasColumnName("blog_id");

    modelBuilder.Entity<Blog>(
        eb =>
        {
            eb.Property(b => b.Url).HasColumnType("varchar(200)");
            eb.Property(b => b.Rating).HasColumnType("decimal(5, 2)");
        });
}
# 分组配置
public class BlogEntityTypeConfiguration : IEntityTypeConfiguration<Blog>
{
    public void Configure(EntityTypeBuilder<Blog> builder)
    {
        builder
            .Property(b => b.Url)
            .IsRequired();
    }
}

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



# 查询数据  关联查询Include
支持的操作包括:Where、OrderBy、OrderByDescending、ThenBy、ThenByDescending、Skip 和 Take。
var blogs = context.Blogs.ToList();//加载所有数据
.Include(blog => blog.Posts).ToList();//Posts所有数据
.Single(b => b.BlogId == 1);//加载单个实体
.SingleOrDefault(b => b.BlogId == 1);//
.Where(b => b.Url.Contains("dotnet")).ToList();//筛选
.OrderByDescending(blog => blog.Rating).Select(
        blog => new { Id = blog.BlogId, Url = StandardizeUrl(blog.Url) })
    .ToList();////排序
.Include(
            blog => blog.Posts
                .Where(post => post.BlogId == 1)
                .OrderByDescending(post => post.Title)
                .Take(5))
        .ToList();
context.Blogs.Add(blog);//添加数据
context.Blogs.Remove(blog);//删除数据
context.SaveChanges();//保存

二、简单的案例使用1(推荐参考最佳)

第一个EFCore应用

连接mysql(Pomelo.EntityFrameworkCore.MySql)
string connection = "server=127.0.0.1;userid=root;pwd=123456;port=3306;database=blogging;sslmode=none;Convert Zero Datetime=True";//
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseMySql(connection, new MySqlServerVersion(new System.Version(5, 7, 37)));

1.创建一个控制台程序
2.添加包
Microsoft.EntityFrameworkCore.Sqlite
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Tools
3.增加model类Model.cs //是一对多的
public class BloggingContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
    public string DbPath { get; }
    public BloggingContext()
    {
        var folder = Environment.SpecialFolder.LocalApplicationData;
        var path = Environment.GetFolderPath(folder);//
        DbPath = System.IO.Path.Join(path, "blogging.db");
    }

    // The following configures EF to create a Sqlite database file in the
    // special "local" folder for your platform.
    protected override void OnConfiguring(DbContextOptionsBuilder options)
        => options.UseSqlite($"Data Source={DbPath}");
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
    
    public List<Post> Posts { get; } = new();
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}

4.在程序包管理器控制台
Add-Migration FirstMigration
Update-Database

5.Program.cs(进行CRUD)
using (var db = new BloggingContext())
{
    // Note: dababase path
    Console.WriteLine($"Database path: {db.DbPath}.");

    //Create
    //Console.WriteLine("Inserting a new blog");
    //db.Add(new Blog { Url = "http://baidu.com" });//http://blogs.msdn.com/adonet;
    //db.SaveChanges();

    // Read
    Console.WriteLine("Querying for a blog");
    var blog = db.Blogs
        .OrderBy(b => b.BlogId)
        .FirstOrDefault();
    //var blogs = db.Blogs.Where(b => b.BlogId > 1);
    //foreach (var item in blogs)
    //{
    //    Console.WriteLine($"{item.Url}");
    //}

    // Update
    Console.WriteLine("Updating the blog and adding a post");
    blog!.Url = "https://devblogs.microsoft.com/dotnet";
    blog.Posts.Add(
        new Post { Title = "Hello World", Content = "I wrote an app using EF Core!" });
    db.SaveChanges();

    //// Delete
    Console.WriteLine("Delete the blog");
    db.Remove(blog);
    db.SaveChanges();
}

三、简单的案例使用2

命令行

//需要添加所需的包
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Design//添加 EF Core 工具所需的包。
dotnet tool install --global dotnet-ef//将安装 dotnet ef,用于创建迁移和基架的工具。

dotnet ef migrations add InitialCreate --context PizzaContext//创建数据库的迁移
dotnet ef database update --context PizzaContext//将应用迁移生成数据库。
  1. 在项目根目录中,添加一个名为 Data 的新文件夹。
  2. 在 Data 目录中,创建一个名为 PizzaContext.cs 的新文件。 将以下代码添加到空文件中:
  3. 在 Program.cs 中,将 // Add the PizzaContext
using Microsoft.EntityFrameworkCore;
using ContosoPizza.Models;

namespace ContosoPizza.Data;

public class PizzaContext : DbContext
{
    public PizzaContext (DbContextOptions<PizzaContext> options)
        : base(options)
    {
    }

    public DbSet<Pizza> Pizzas => Set<Pizza>();//表名
    public DbSet<Topping> Toppings => Set<Topping>();
    public DbSet<Sauce> Sauces => Set<Sauce>();
}

//-------------------
builder.Services.AddSqlite<PizzaContext>("Data Source=ContosoPizza.db");

四、简单的案例使用3

两种配置方式

1、Data Annotation
把配置以特性(Annotation)的形式标注在实体类中。
[Table("T_Books")]
public class Book
{
}
优点:简单;缺点:耦合。
2、Fluent API
builder.ToTable("T_Books");
把配置写到单独的配置类中。
缺点:复杂;优点:解耦

Guid主键
1、 Guid算法(或UUID算法)生成一个全局唯一的Id。适合于分布式系统,在进行多数据库数据合并的时候很简单。优点:简单,高并发,全局唯一;缺点:磁盘空间占用大。

yzk开源的高效批量修改、删除的开源项目Zack.EFCore.Batch
1、经典步骤:建实体类;建DbContext;生成数据库;编写调用EF Core的业务代码。

程序包管理器控制台
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Pomelo.EntityFrameworkCore.MySql
optionsBuilder.UseMySql("server=localhost;user=root;password=root;database=ef",
	new MySqlServerVersion(new Version(5, 6, 0)));
Install-Package Npgsql.EntityFrameworkCore.PostgreSQL
optionsBuilder.UseNpgsql("Host=127.0.0.1;Database=ef;Username=postgres;Password=123456");

在这里插入图片描述

//Book.cs
public class Book
{
     public long Id { get; set; }//主键
    [Required]
    [MaxLength(100)]
     public string Title { get; set; }//标题
     public DateTime PubTime { get; set; }//发布日期
     public double Price { get; set; }//单价
 }
 //创建实现了IEntityTypeConfiguration接口的实体配置类,配置实体类和数据库表的对应关系
class BookEntityConfig : IEntityTypeConfiguration<Book>
{
	public void Configure(EntityTypeBuilder<Book> builder)
	{
		builder.ToTable("T_Books");
		//builder.Property(e => e.Title).HasMaxLength(50).IsRequired();
		//builder.Property(e => e.AuthorName).HasMaxLength(20).IsRequired();
	}
}
//创建继承自DbContext的类
class TestDbContext:DbContext
{
	public DbSet<Book> Books { get; set; }
	protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
	{
		string connStr = "Server=.;Database=demo1;Trusted_Connection=True;MultipleActiveResultSets=true";
		optionsBuilder.UseSqlServer(connStr);
	}
	protected override void OnModelCreating(ModelBuilder modelBuilder)
	{
		base.OnModelCreating(modelBuilder);
		modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
	}
}
//插入数据
//只要操作Books属性,就可以向数据库中增加数据,但是通过C#代码修改Books中的数据只是修改了内存中的数据。对Books做修改后,需要调用DbContext的异步方法SaveChangesAsync()把修改保存到数据库。也有同步的保存方法SaveChanges(),但是用EF Core都推荐用异步方法。
//查询数据
1、DbSet实现了IEnumerable<T>接口,因此可以对DbSet实施Linq操作来进行数据查询。EF Core会把Linq操作转换为SQL语句。面向对象,而不是面向数据库(SQL)。
2、ctx.Books.Where(b => b.Price > 80)
Book b1 = ctx.Books.Single(b => b.Title== "零基础趣学C语言");
Book b2 = ctx.Books.FirstOrDefault(b=>b.Id==9);
3、可以使用OrderBy操作进行数据的排序
IEnumerable<Book> books = ctx.Books.OrderByDescending(b => b.Price);
//查询数据2
1、GroupBy也可以
var groups = ctx.Books.GroupBy(b => b.AuthorName)
	.Select(g => new { AuthorName = g.Key, BooksCount = g.Count(), MaxPrice = g.Max(b => b.Price) });
foreach(var g in groups)
{
	Console.WriteLine($"作者名:{g.AuthorName},著作数量:{g.BooksCount},最贵的价格:{g.MaxPrice}");
}
2、大部分Linq操作都能作用于EF Core。
//修改、删除
1、要对数据进行修改,首先需要把要修改的数据查询出来,然后再对查询出来的对象进行修改,然后再执行SaveChangesAsync()保存修改。
var b = ctx.Books.Single(b=>b.Title== "数学之美");
b.AuthorName = "Jun Wu";
await ctx.SaveChangesAsync();
2、删除也是先把要修改的数据查询出来,然后再调用DbSet或者DbContext的Remove方法把对象删除,然后再执行SaveChangesAsync()保存修改。
var b = ctx.Books.Single(b => b.Title == "数学之美");
ctx.Remove(b);//也可以写成ctx.Books.Remove(b);
await ctx.SaveChangesAsync();
//批量修改、删除 性能低:查出来,再一条条Update、Delete,

五、身份验证(mvc,ef)

ASP.NET Core MVC 登录验证
1,创建一个mvc的项目
2,添加包,创建一个实体类,相应的上下文类。
3,数据库的迁移,实现,program.cs的配置。
4,在HomeController添加[Authorize],
5, 添加一个User控制器,以及视图Login,添加内容_Layout.cshtml

Microsoft.EntityFrameworkCore.Sqlite
Pomelo.EntityFrameworkCore.MySql
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Tools
Add-Migration InitialCreate//实现迁移
Update-database//生成数据库
//User.cs
public class User
{
     public int Id { get; set; }
     public string Account { get; set; }
     public string Password { get; set; }
 }
//MyDbContext.cs
using Microsoft.EntityFrameworkCore;
public class MyDbContext:DbContext
{
    public DbSet<User> Users { get; set; }
    public MyDbContext(DbContextOptions options) : base(options)
    {

    }
}
//program.cs
//builder.Services.AddSqlite<MyDbContext>("Data Source=ContosoPizza.db");//连接Sqlite
builder.Services.AddDbContext<MyDbContext>(options=>options.UseMySql("server=localhost;user=root;password=123456;database=Myhaha",
    new MySqlServerVersion(new Version(5, 6, 0))));
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)//身份验证
    .AddCookie(options =>
    {
        options.LoginPath = "/User/Login";//初识路径
    });
app.UseAuthentication();

//UserController.cs
public class UserController : Controller
{
    private readonly MyDbContext _context;
    public UserController(MyDbContext myDbContext)
    {
        _context = myDbContext;
    }
    public IActionResult Index()
    {
        return View();
    }
    public IActionResult Login(string returnUrl)
    {
        ViewData["ReturnUrl"] = returnUrl;
        return View();
    }
    [HttpPost]
    public async Task< IActionResult> Login(UserLoginRequest request)
    {
        if(await _context.Users.AnyAsync(a => a.Account == request.Account && a.Password == request.Password))
            //if (request.Account=="admin"&&request.Password=="admin")
        {
            var claim = new List<Claim>()
            {
                new Claim(ClaimTypes.Name,request.Account),//保存了数据。
            };
            var clainmnsIdentity=new ClaimsIdentity(claim, CookieAuthenticationDefaults.AuthenticationScheme);
            await HttpContext.SignInAsync(
                CookieAuthenticationDefaults.AuthenticationScheme,
                new ClaimsPrincipal(clainmnsIdentity),
                new AuthenticationProperties
                {
                    IsPersistent = true,//持久cookie
                });
        }
        else{ return RedirectToAction(nameof(Login));}
       return Redirect(request.ReturnUrl ?? "/");
       //return RedirectToAction(nameof(Index));
    }
    public async Task<IActionResult> Logout()
    {
        await HttpContext.SignOutAsync();
        return RedirectToAction(nameof(Login));
    }
}
public class UserLoginRequest : User
{
    public string ReturnUrl { get; set; }
}

//Login.cshtml
<form asp-action="Login" method="post">
    <input type="hidden" name="returnUrl" value="@ViewBag.ReturnUrl" />
    <input type="text" name="account" value="" />
    <input type="password" name="password" value="" />

    <input type="submit"  value="登录" />
</form>
//_Layout.cshtml
添加内容
<li class="nav-item">
    @User.Identity.Name
</li>
<li class="nav-item">
    <a class="nav-link text-dark" asp-area="" asp-controller="User" asp-action="Logout">退出登录</a>
</li>

其他

1.数据库表之间的关系

数据库表之间的关系:一对一、一对多、多对多

一对多:HasOne(…).WithMany(…);
一对一:HasOne(…).WithOne (…);
多对多:HasMany (…).WithMany(…);

1、IQueryable books = ctx.Books;
books.Where(b => b.Price > 1.1)
2、IEnumerable books = ctx.Books;
books.Where(b => b.Price > 1.1)

  1. 一对多:实体类,一对多:关系配置
//1、文章实体类Article、评论实体类Comment。一篇文章对应多条评论。
public class Article
{
	public long Id { get; set; }
	public string Title { get; set; }
	public string Content { get; set; }
	public List<Comment> Comments { get; set; } 
                       = new List<Comment>(); 
}
public class Comment
{
	public long Id { get; set; }
	public Article Article { get; set; }
	public string Message { get; set; }
}
//一对多:关系配置
class ArticleConfig : IEntityTypeConfiguration<Article>
{
	public void Configure(EntityTypeBuilder<Article> builder)
	{
		builder.ToTable("T_Articles");
		builder.Property(a => a.Content).IsRequired().IsUnicode();
		builder.Property(a => a.Title).IsRequired().IsUnicode().HasMaxLength(255);
	}
}
class CommentConfig : IEntityTypeConfiguration<Comment>
{
	public void Configure(EntityTypeBuilder<Comment> builder)
	{
		builder.ToTable("T_Comments");
		builder.HasOne<Article>(c=>c.Article).WithMany(a => a.Comments).IsRequired();//HasForeignKey(c=>c.ArticleId)
		builder.Property(c=>c.Message).IsRequired().IsUnicode();
	}
}


//获取关系数据
Article a = ctx.Articles.Include(a=>a.Comments).Single(a=>a.Id==1);
Console.WriteLine(a.Title);
ctx.Articles.Where(a=>a.Comments.Any(c=>c.Message.Contains("微软")));
ctx.Comments.Where(c => c.Message.Contains("微软"))
.Select(c => c.Article).Distinct();
foreach(Comment c in a.Comments)
{
	Console.WriteLine(c.Id+":"+c.Message);
}
//Include定义在Microsoft.EntityFrameworkCore命名空间中
//关联查询

//CommentConfig:
builder.HasOne<Article>(c=>c.Article).WithMany(a => a.Comments).IsRequired();
//ArticleConfig:
builder.HasMany<Comment>(a => a.Comments).WithOne(c => c.Article).IsRequired();
  1. 一对一关系
class Order
{
	public long Id { get; set; }
	public string Name { get; set; }
	public string Address { get; set; }
	public Delivery Delivery { get; set;} 
}

class Delivery
{
	public long Id { get; set; }
	public string CompanyName { get; set; }
	public String Number { get; set; }
	public Order Order { get; set; }
	public long OrderId { get; set; }
}

1、builder.HasOne<Delivery>(o => o.Delivery).WithOne(d => d.Order).HasForeignKey<Delivery>(d=>d.OrderId);
2、测试插入和获取数据。
  1. 多对多(老师—学生。)
class Student
{
	public long Id { get; set; }
	public string Name { get; set; }
	public List<Teacher> Teachers { get; set; } = new List<Teacher>();
}
class Teacher
{
	public long Id { get; set; }
	public string Name { get; set; }
	public List<Student> Students { get; set; } = new List<Student>();
}

builder.HasMany<Teacher>(s => s.Teachers).WithMany(t=>t.Students).UsingEntity(j=>j.ToTable("T_Students_Teachers"));

2.所谓IQueryable

1、IQueryable只是代表一个“可以放到数据库服务器去执行的查询”,它没有立即执行,只是“可以被执行”而已。
2、对于IQueryable接口调用非终结方法的时候不会执行查询,而调用终结方法的时候则会立即执行查询。
3、终结方法:遍历、ToArray()、ToList()、Min()、Max()、Count()等;
4、非终结方法:GroupBy()、OrderBy()、Include()、Skip()、Take()等。
5、简单判断:一个方法的返回值类型如果是IQueryable类型,那么这个方法一般就是非终结方法,否则就是终结方法。

1、IQueryable是一个待查询的逻辑,因此它是可以被重复使用的。
2、IQueryable books = ctx.Books.Where(b => b.Price <= 8);
Console.WriteLine(books.Count());
Console.WriteLine(books.Max(b=>b.Price));
var books2 = books.Where(b=>b.PubTime.Year>2000);

1、DataReader:分批从数据库服务器读取数据。内存占用小、 DB连接占用时间长;
2、DataTable:把所有数据都一次性从数据库服务器都加载到客户端内存中。内存占用大,节省DB连接。
验证IQueryable用什么方式

1、用insert into select多插入一些数据,然后加上Delay/Sleep的遍历IQueryable。在遍历执行的过程中,停止SQLServer服务器。
IQueryable内部就是在调用DataReader。
2、优点:节省客户端内存。
缺点:如果处理的慢,会长时间占用连接。
//如何一次性加载数据到内存
1、一次性加载数据到内存:用IQueryable的ToArray()、ToArrayAsync()、ToList()、ToListAsync()等方法。
2、等ToArray()执行完毕,再断服务器试一下。
//何时需要一次性加载
1、场景1:遍历IQueryable并且进行数据处理的过程很耗时。
2、场景2:如果方法需要返回查询结果,并且在方法里销毁DbContext的话,是不能返回IQueryable的。必须一次性加载返回。
3、场景3:多个IQueryable的遍历嵌套。很多数据库的ADO.NET Core Provider是不支持多个DataReader同时执行的。把连接字符串中的MultipleActiveResultSets=true删掉,其他数据库不支持这个。
//对应异步方法
1、SaveChanges()、SaveChangesAsync()
2、异步方法大部分是定义在Microsoft.EntityFrameworkCore这个命名空间下EntityFrameworkQueryableExtensions等类中的扩展方法,记得using。
3、AddAsync()、AddRangeAsync()、AllAsync()、AnyAsync、AverageAsync、ContainsAsync、CountAsync、FirstAsync、FirstOrDefaultAsync、ForEachAsync、LongCountAsync、MaxAsync、MinAsync、SingleAsync、SingleOrDefaultAsync、SumAsync等

Dapper ORM

https://dapper-tutorial.net/step-by-step-tutorial
https://www.w3cschool.cn/dapperorm/

https://www.jb51.net/article/216497.htm
https://www.codenong.com/cs106392582/
https://www.cnblogs.com/yixuanhan/p/9238781.html
https://blog.csdn.net/dxm809/article/details/112038117

简单案例

using Dapper;
using MySql.Data.MySqlClient;
using System.Data;
using System.Data.SqlClient;
List<User> user = new List<User>();
string strcon = "Database=traytest;Data Source=localhost;User Id=root;Password=123456;CharSet=utf8;port=3306";
IDbConnection db = new MySqlConnection(strcon);
db.Open();
//queryid(28);

Insert(new User {  name= "2222",age="2222" }); 
//查询
void query()
{
    user = db.Query<User>("Select * From user").ToList();
    foreach (var item in user)
    {
        Console.WriteLine("name=" + item.name + "~~~age=" + item.age);
    }
}
void queryid(int id)
{
    user = db.Query<User>($"Select * From user where id={id}").ToList();
    foreach (var item in user)
    {
        Console.WriteLine("name=" + item.name + "~~~age=" + item.age);
    }
}
//添加
void Insert(User user)
{
    string insertSqlStr = $"INSERT INTO  user(id,Name,age)VALUES(null,{user.name},{user.age})";
    //db.Execute(insertSqlStr, user); //这种是按实体插入
    db.Execute(insertSqlStr); //这种是按实体插入
}

public class User
{
    public int id { get; set; }
    public string name { get; set; }
    public string age { get; set; }

}


-------------------
        //查询
        #region Query
        void queryid(int id)
        {
            user = db.Query<User>($"Select * From user where id={id}").ToList();
            foreach (var item in user)
            {
                Console.WriteLine("name=" + item.name + "~~~age=" + item.age);
            }
        }
        #endregion

        #region Update
        //更新
        void Update(User user)
        {
            string sql = "UPDATE user SET name = @name,age=@age WHERE id = (@id)";
            db.Execute(sql, user); //这种是按实体插入
            Console.WriteLine("更新成功");
        }
        //更新多个
        void UpdateNumbs(List<User> users)
        {
            string sql = "UPDATE user SET name = @name,age=@age WHERE id = (@id)";
            db.Execute(sql, users); //这种是按实体插入
            Console.WriteLine("更新成功多个");
        }
        #endregion

        #region Delete
        //删除
        void Delete(User user)
        {
            string sql = "DELETE FROM user WHERE id = @id";
            db.Execute(sql, user); //这种是按实体删除
            Console.WriteLine("删除成功");
        }
        //删除多个
        void DeleteNumbs(List<User> users)
        {
            string sql = "DELETE FROM user WHERE id = @id";
            db.Execute(sql, users); //这种是按实体删除多个
            Console.WriteLine("删除多个");
        }

        #endregion

        #region Add
        //添加
        void Insert(User user)
        {
            //string insertSqlStr = $"INSERT INTO  user(id,name,age)VALUES(null,{user.name},{user.age})";
            string insertSqlStr = $"INSERT INTO  user(id,name,age)VALUES(null,(@name),(@age))";
            //db.Execute(insertSqlStr, user); //这种是按实体插入
            db.Execute(insertSqlStr, user); //这种是按实体插入
            Console.WriteLine("添加成功");
        }
        //添加多个
        void InsertNumbs(List<User> users)
        {
            string insertSqlStr = $"INSERT INTO  user(id,name,age)VALUES(null,(@name),(@age))";
            //db.Execute(insertSqlStr, user); //这种是按实体插入
            db.Execute(insertSqlStr, users); //这种是按实体插入
            Console.WriteLine("添加多个成功");
        }
        #endregion

SqlSugar ORM

官网

简单案例

//查询所有
using SqlSugar;
using MySql.Data;
Demo();

static void Demo()
{

    //创建数据库对象
    SqlSugarClient db = new SqlSugarClient(new ConnectionConfig()
    {
        ConnectionString = "Database=traytest;Data Source=localhost;User Id=root;Password=123456;CharSet=utf8;port=3306",
        //ConnectionString = "server=localhost;Database=traytest;Uid=root;Pwd=123456; AllowLoadLocalInfile=true",
        DbType = DbType.MySql,
        //DbType = DbType.MySqlConnector,
        IsAutoCloseConnection = true
    });

    //调试SQL事件,可以删掉(要放在执行方法之前)
    db.Aop.OnLogExecuting = (sql, pars) =>
    {
        Console.WriteLine(sql);//输出sql,查看执行sql
                               //5.0.8.2 获取无参数化 SQL 
                               //UtilMethods.GetSqlString(DbType.SqlServer,sql,pars)
    };

    //查询表的所有
    //var list = db.Queryable<User>().ToList();
    //foreach (var item in list)
    //{
    //    Console.WriteLine("name=" + item.Name + "~~~age=" + item.Age);
    //}

    //插入
    //db.Insertable(new User() { Age="jack", Name = "jack" }).ExecuteCommand();

    //更新
    //db.Updateable(new User() { Id = 30, Age = "jack2", Name = "jack2" }).ExecuteCommand();

    //删除
    //db.Deleteable<User>().Where(it => it.Id == 30).ExecuteCommand();

}



//实体与数据库结构一样
public class User
{
    //数据是自增需要加上IsIdentity 
    //数据库是主键需要加上IsPrimaryKey 
    //注意:要完全和数据库一致2个属性
    [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
    public int Id { get; set; }
    public string Age { get; set; }
    public string Name { get; set; }
}

Freesql ORM

官网

简单案例

IFreeSql fsql = new FreeSql.FreeSqlBuilder()
    .UseConnectionString(FreeSql.DataType.MySql, "Database=traytest;Data Source=localhost;User Id=root;Password=123456;CharSet=utf8;port=3306")
    .UseAutoSyncStructure(true) //自动同步实体结构到数据库,FreeSql不会扫描程序集,只有CRUD时才会生成表。
    .Build(); //请务必定义成 Singleton 单例模式
List<User> select = fsql.Select<User>().ToList();
foreach (var item in select)
{
    Console.WriteLine(item.Age);
}
//实体与数据库结构一样
public class User
{
    public int Id { get; set; }
    public string Age { get; set; }
    public string Name { get; set; }
}

ADO.NET(不是ORM)

参考链接:
官方文档
易百教程
asp.net连接mysql数据库+mysql公共类
https://blog.csdn.net/qq_23018459/article/details/79446935
对理解有帮助
c#中用来批量更新数据库SqlCommandBuilder

  • 由于Ado直接操作数据库,性能开销最小;而ORM架构大多使用反射来获取对象属性,然后映射成数据字段,或者反过来把字段映射成属性,反射有性能上的开销。
  • 五大对象:
    Connection(连接对象)
    Command(命令对象)
    DataReader(只读,数据库)
    DataAdapter(数据适配器)
    DataSet(数据集对象)
posted @ 2022-04-27 18:46  cactus9  阅读(186)  评论(0编辑  收藏  举报