ORM
ORM(对象关系映射)是一种编程技术,用于在关系型数据库和面向对象编程语言之间建立映射关系。ORM 工具能够将数据库中的表、视图和存储过程等数据结构映射到编程语言中的对象模型,使得开发人员可以使用面向对象的方式来操作数据库,而不必直接编写 SQL 查询语句
ORM 的主要目标是简化数据库操作,它提供了一种更抽象的方式来处理数据库,隐藏了数据库底层细节
面向对象编程: 将数据库表映射为编程语言中的对象,使得开发者可以使用面向对象的方式来处理数据,提高了代码的可读性和可维护性。
减少重复性工作: ORM 工具自动生成数据库操作的代码,减少了手动编写重复性 CRUD(创建、读取、更新、删除)操作的工作量。
数据库平台无关性: ORM 工具可以支持多种数据库类型,使得应用程序更易于迁移和适应不同的数据库平台。
提高生产力: ORM 工具提供了一些高级功能,如查询构建器、事务管理、缓存等,能够加快开发速度并提高效率
示例
- 对象:c#中的对象
- 关系:关系数据库
- 映射:关系数据库和c#对象之间搭建的一座桥梁
如下通过创建c#对象的方式把数据插入数据库,省去编写Insert语句:
User user = new User(){Name="li",Password="123456"};
orm.Save(user);
注意:ORM只是对ADO.NET的封装,ORM底层任然是通过ADO.NET来访问数据库
EF Core
Entity Framework (EF) Core 是流行的 Entity Framework 数据访问技术的轻量级、可扩展、开源和跨平台版本
模型
使用 EF Core,可以使用 模型 执行数据访问,模型 由 实体类 和 表示与数据库的会话的上下文 对象组,上下文对象允许查询和保存数据
EF Core用于将对象和数据库中的表进行映射,因此需要创建实体类和数据库表两项内容
EF 支持以下模型开发方法:
- 从现有数据库生成模型
- 手动编写模型以匹配数据库
- 创建模型后,使用 EF 迁移 从模型 创建数据库
- 迁移允许随着模型的变化而发展数据库
构建一个简单的模型
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
namespace Intro;
public class BloggingContext : DbContext
{
/*
继承自 DbContext 的数据库上下文类中使用 DbSet<> 属性来表示数据库中的一个表或一个实体 集合
Add(): 向集合中添加新实体
Remove(): 从集合中移除指定的实体
Find(): 根据主键值查找实体
ToList(): 将集合中的实体查询为列表
*/
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public string DbPath { get; }
public BloggingContext()
{
// Environment.SpecialFolder.LocalApplicationData 表示本地应用程序数据文件夹。这是一个特殊的文件夹路径,通常用于存储应用程序的数据
var folder = Environment.SpecialFolder.LocalApplicationData;
// Environment.GetFolderPath(folder) 方法获取指定特殊文件夹的路径,fan
var path = Environment.GetFolderPath(folder);
DbPath = System.IO.Path.Join(path, "blogging.db");
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
/*
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True");
optionsBuilder.UseSqlServer 是 Entity Framework Core 中用于配置数据库提供程序的方法之一
Server:指定 SQL Server 的位置。在这里是本地的 SQL Server 实例 (localdb)\mssqllocaldb。
Database:指定要连接的数据库名称为 Blogging。
Trusted_Connection=True:使用 Windows 身份验证来建立连接(可信连接)
*/
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite($"Data Source={DbPath}");
}
}
// 创建了 Blog 实体
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public int Rating { get; set; }
public List<Post> Posts { get; set; }
}
// 创建了 Post 实体
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; }
}
创建、读取、更新和删除
using System;
using System.Linq;
using var db = new BloggingContext();
// Note: This sample requires the database to be created before running.
Console.WriteLine($"Database path: {db.DbPath}.");
// Create
Console.WriteLine("Inserting a new blog");
db.Add(new Blog { Url = "http://blogs.msdn.com/adonet" });
db.SaveChanges();
// Read
Console.WriteLine("Querying for a blog");
var blog = db.Blogs.OrderBy(b => b.BlogId).First();
// 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();
查询
保存
使用实体类的实例在数据库中创建、删除和修改数据
using (var db = new BloggingContext())
{
var blog = new Blog { Url = "http://sample.com" };
db.Blogs.Add(blog);
db.SaveChanges();
}
// 更改跟踪和SaveChanges
using (var context = new BloggingContext())
{
var blog = context.Blogs.Single(b => b.Url == "http://example.com");
blog.Url = "http://example.com/blog";
context.SaveChanges();
}
// 执行更新和执行删除(批量更新)
// 1. 使用ExecuteDelete
context.Blogs.Where(b => b.Rating < 3).ExecuteDelete();
// 2. 通过常规 LINQ 运算符表达 SQLDELETE语句 - 类似于常规 LINQ 查询 - 导致对数据库执行以下 SQL
DELETE FROM [b]
FROM [Blogs] AS [b]
WHERE [b].[Rating] < 3
//更新
// 1.
context.Blogs
.Where(b => b.Rating < 3)
.ExecuteUpdate(setters => setters.SetProperty(b => b.IsVisible, false));
// 2.
UPDATE [b]
SET [b].[IsVisible] = CAST(0 AS bit)
FROM [Blogs] AS [b]
WHERE [b].[Rating] < 3
添加新实体的图表
创建多个新的相关实体,将其中一个添加到上下文中将导致其他实体也被添加
using (var context = new BloggingContext())
{
var blog = new Blog
{
Url = "http://blogs.msdn.com/dotnet",
Posts = new List<Post>
{
new Post { Title = "Intro to C#" },
new Post { Title = "Intro to VB.NET" },
new Post { Title = "Intro to F#" }
}
};
context.Blogs.Add(blog);
context.SaveChanges();
}
//使用 EntityEntry.State 属性仅设置单个实体的状态。例如,context.Entry(blog).State = EntityState.Modified
添加相关实体
从上下文已跟踪的实体的导航属性引用新实体,则该实体将被发现并插入到数据库中
using (var context = new BloggingContext())
{
var blog = context.Blogs.Include(b => b.Posts).First();
var post = new Post { Title = "Intro to EF Core" };
blog.Posts.Add(post);
context.SaveChanges();
}
改变关系
更改实体的导航属性,数据库中的外键列也会发生相应的更改
using (var context = new BloggingContext())
{
var blog = new Blog { Url = "http://blogs.msdn.com/visualstudio" };
var post = context.Posts.First();
post.Blog = blog;
context.SaveChanges();
}
删除关系
null :可以通过将引用导航设置为或从集合导航中删除相关实体来删除关系
根据关系中配置的级联删除行为,删除关系可能会对依赖实体产生副作用
默认情况下,对于所需的关系,会配置级联删除行为,并且将从数据库中删除子/依赖实体。对于可选关系,默认不配置级联删除,但外键属性将设置为 null
请参阅必需和可选关系以了解如何配置关系的必需性
有关级联删除行为如何工作、如何显式配置以及如何按约定选择它们的更多详细信息,请参阅级联删除
using (var context = new BloggingContext())
{
var blog = context.Blogs.Include(b => b.Posts).First();
var post = blog.Posts.First();
blog.Posts.Remove(post);
context.SaveChanges();
}
级联删除
Entity Framework Core (EF Core) 使用外键表示关系。具有外键的实体是关系中的子实体或依赖实体。该实体的外键值必须与相关主体/父实体的主键值(或备用键值)匹配
如果删除主体/父实体,则从属/子实体的外键值将不再与任何主体/父实体的主键或备用键匹配。这是无效状态,并且会在大多数数据库中导致引用约束违规
有两个选项可以避免违反引用约束:
- 将 FK 值设置为 null
- 同时删除依赖/子实体
第一个选项仅对外键属性(及其映射到的数据库列)必须可为空的可选关系有效
第二个选项对任何类型的关系都有效,称为“级联删除”
当级联行为发生时
依赖/子实体不再与其当前主体/父实体关联时,需要级联删除。发生这种情况的原因可能是主体/父项被删除,或者当主体/父项仍然存在但依赖项/子项不再与其关联时,可能会发生这种情况
删除级联
// 考虑以上模型,其中Blog是 的关系中的主体/父项Post, 是从属/子项。Post.BlogId是一个外键属性,其值必须Blog.Id与帖子所属博客的主键匹配
// 按照约定,Post.BlogId 是Bolg外键关系被配置为必需的,因为Post.BlogId外键属性不可为空。默认情况下,所需关系配置为使用级联删除
using var context = new BlogsContext();
var blog = context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).First();
context.Remove(blog);
context.SaveChanges();
断绝关系
可以切断表之间的关系,通过将表的参考导航设置为 null 来完成
using var context = new BlogsContext();
var blog = context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).First();
foreach (var post in blog.Posts)
{
post.Blog = null;
}
context.SaveChanges();
或者刪除表之间联系的一个表外键来切断关系
using var context = new BlogsContext();
var blog = context.Blogs.OrderBy(e => e.Name).Include(e => e.Posts).First();
blog.Posts.Clear();
context.SaveChanges();
ps: 级联删除: https://learn.microsoft.com/en-us/ef/core/saving/cascade-delete