VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > c#编程 >
  • EF Core入门:ORM映射与查询

EF Core入门:ORM映射与查询
概述
EF Core(Entity Framework Core)是.NET生态中最流行的ORM框架(对象关系映射),它将C#实体类与数据库表关联,让开发者用面向对象的方式操作数据库,无需手写大量SQL。核心优势包括:
消除手写SQL的繁琐,降低出错概率;
支持多种数据库(SQL Server、MySQL、PostgreSQL等);
内置LINQ查询,无缝集成C#语法;
自动管理数据库连接与事务。
本节通过完整实例讲解EF Core的核心用法:实体映射、DbContext配置、CRUDvb.net教程C#教程python教程SQL教程access 2010教程
操作、LINQ查询,重点覆盖映射规则和查询优化。

  1. 实例代码:完整的ORM映射与查询流程
    以下实例定义User和Order两个实体(一对多关系),通过EF Core完成数据库创建、数据增删改查、关联查询:
    csharp
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Threading.Tasks;
	using Microsoft.EntityFrameworkCore; // EF Core核心包(需NuGet安装)
	
	// -------------------------- 1. 定义实体类 --------------------------
	public class User
	{
	// 主键(自增)
	public int Id { get; set; }
	// 用户名(非空,最大长度50)
	public string Name { get; set; }
	// 邮箱(唯一,最大长度100)
	public string Email { get; set; }
	// 导航属性:一个用户有多个订单
	public List<Order> Orders { get; set; } = new List<Order>(); // 初始化避免null
	}
	
	public class Order
	{
	// 主键(自增)
	public int Id { get; set; }
	// 订单号(非空,最大长度20)
	public string OrderNumber { get; set; }
	// 订单日期(默认当前时间)
	public DateTime OrderDate { get; set; }
	// 订单金额
	public decimal TotalAmount { get; set; }
	// 外键:关联User表
	public int UserId { get; set; }
	// 导航属性:一个订单属于一个用户
	public User User { get; set; }
	}
	
	// -------------------------- 2. 配置DbContext(数据库会话) --------------------------
	public class AppDbContext : DbContext
	{
	// 对应数据库表:Users、Orders
	public DbSet<User> Users { get; set; }
	public DbSet<Order> Orders { get; set; }
	
	// 配置数据库连接(开发环境,生产环境用配置文件)
	protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
	{
	optionsBuilder
	.UseSqlServer("Server=localhost;Database=EFCoreDemo;User Id=sa;Password=YourPassword;TrustServerCertificate=True;")
	.LogTo(Console.WriteLine); // 输出生成的SQL(调试用)
	}
	
	// 配置实体映射(Fluent API)
	protected override void OnModelCreating(ModelBuilder modelBuilder)
	{
	// 配置User实体
	modelBuilder.Entity<User>()
	.HasKey(u => u.Id) // 主键
	.HasName("PK_Users"); // 主键约束名
	modelBuilder.Entity<User>()
	.Property(u => u.Name)
	.IsRequired() // 非空
	.HasMaxLength(50) // 最大长度50
	.HasColumnType("nvarchar(50)"); // 列类型
	modelBuilder.Entity<User>()
	.Property(u => u.Email)
	.IsRequired()
	.HasMaxLength(100)
	.HasColumnType("varchar(100)")
	.IsUnicode(false); // 非Unicode(节省空间)
	
	// 配置Order实体
	modelBuilder.Entity<Order>()
	.HasKey(o => o.Id);
	modelBuilder.Entity<Order>()
	.Property(o => o.OrderNumber)
	.IsRequired()
	.HasMaxLength(20);
	modelBuilder.Entity<Order>()
	.Property(o => o.OrderDate)
	.HasDefaultValueSql("GETDATE()"); // 默认值:当前时间
	modelBuilder.Entity<Order>()
	.Property(o => o.TotalAmount)
	.HasColumnType("decimal(18,2)"); // 精度18,小数位2
	
	// 配置一对多关系:User → Orders
	modelBuilder.Entity<Order>()
	.HasOne(o => o.User) // 订单属于一个用户
	.WithMany(u => u.Orders) // 用户有多个订单
	.HasForeignKey(o => o.UserId) // 外键
	.HasConstraintName("FK_Orders_Users_UserId") // 外键约束名
	.OnDelete(DeleteBehavior.Cascade); // 删除用户时级联删除订单
	}
	}
	
	// -------------------------- 3. 执行CRUD与查询 --------------------------
	class EFCoreExample
	{
	static async Task Main(string[] args)
	{
	// 实例化DbContext(轻量级,每次操作创建新实例)
	using var dbContext = new AppDbContext();
	
	// 确保数据库已创建(开发环境用,生产环境用迁移)
	await dbContext.Database.EnsureCreatedAsync();
	
	// -------------------------- 1. 添加用户与订单 --------------------------
	var newUser = new User
	{
	Name = "张三",
	Email = "zhangsan@example.com",
	Orders = new List<Order>
	{
	new Order { OrderNumber = "ORD2024001", TotalAmount = 199.99m },
	new Order { OrderNumber = "ORD2024002", TotalAmount = 299.99m }
	}
	};
	dbContext.Users.Add(newUser);
	var affectedRows = await dbContext.SaveChangesAsync();
	Console.WriteLine($"添加了{affectedRows}条记录");
	
	// -------------------------- 2. 查询用户及其订单(立即加载) --------------------------
	var userWithOrders = await dbContext.Users
	.Include(u => u.Orders) // 加载导航属性(避免N+1查询)
	.FirstOrDefaultAsync(u => u.Name == "张三");
	if (userWithOrders != null)
	{
	Console.WriteLine($"\n用户:{userWithOrders.Name},订单数:{userWithOrders.Orders.Count}");
	foreach (var order in userWithOrders.Orders)
	{
	Console.WriteLine($"订单号:{order.OrderNumber},金额:{order.TotalAmount:C}");
	}
	}
	
	// -------------------------- 3. 更新用户信息 --------------------------
	if (userWithOrders != null)
	{
	userWithOrders.Email = "zhangsan_new@example.com";
	affectedRows = await dbContext.SaveChangesAsync();
	Console.WriteLine($"\n更新了{affectedRows}条记录");
	}
	
	// -------------------------- 4. 删除订单 --------------------------
	var orderToDelete = await dbContext.Orders
	.FirstOrDefaultAsync(o => o.OrderNumber == "ORD2024001");
	if (orderToDelete != null)
	{
	dbContext.Orders.Remove(orderToDelete);
	affectedRows = await dbContext.SaveChangesAsync();
	Console.WriteLine($"\n删除了{affectedRows}条记录");
	}
	
	// -------------------------- 5. 非跟踪查询(优化性能) --------------------------
	var usersNoTracking = await dbContext.Users
	.AsNoTracking() // 不跟踪实体(只读场景用)
	.Where(u => u.Email.Contains("example.com"))
	.ToListAsync();
	Console.WriteLine($"\n非跟踪查询到{usersNoTracking.Count}个用户");
	}
	}
  1. 逐行讲解
    2.1 实体类定义
    主键:每个实体必须有主键(EF Core默认识别Id或UserId为键);
    导航属性:User.Orders和Order.User表示一对多关系,需初始化(如new List())避免null;
    外键:Order.UserId关联User.Id,是关系的物理体现。
    2.2 DbContext配置
    DbContext:数据库会话的核心,负责实体与数据库的交互;
    DbSet:对应数据库表(如DbSet→Users表);
    OnConfiguring:配置数据库连接(生产环境应从配置文件读取连接字符串);
    OnModelCreating:用Fluent API配置映射(比数据注解更灵活),如主键、列类型、关系约束。
    2.3 CRUD操作
    添加:Add/AddRange添加实体,SaveChangesAsync提交事务;
    查询:用LINQ方法(FirstOrDefaultAsync、Where)查询,Include加载导航属性;
    更新:修改跟踪实体的属性后,SaveChangesAsync自动生成UPDATE语句;
    删除:Remove/RemoveRange标记实体为删除,SaveChangesAsync执行DELETE。
    2.4 关键优化点
    立即加载:Include避免N+1查询(一次JOIN查询获取所有数据);
    非跟踪查询:AsNoTracking不缓存实体,适合只读场景,内存占用更低;
    异步操作:Async后缀方法(如SaveChangesAsync)提高并发性能。
  2. 基础知识拓展
    3.1 映射方式对比
方式 优点 缺点 适用场景
数据注解 简单直观,代码与映射同处实体类 灵活性低,复杂映射支持不足 简单实体(如单表
Fluent API 灵活强大,支持所有映射规则 代码分散在OnModelCreating 复杂实体(如关系、索引)

示例:数据注解映射
csharp

	public class User
	{
	[Key(Name = "PK_Users")]
	public int Id { get; set; }
	[Required, MaxLength(50), Column(TypeName = "nvarchar(50)")]
	public string Name { get; set; }
	[Required, MaxLength(100), Column(TypeName = "varchar(100)", IsUnicode = false)]
	public string Email { get; set; }
	}

3.2 EF Core生命周期
DbContext实例:轻量级,应短期持有(如ASP.NET Core中用Scoped生命周期);
跟踪机制:EF Core默认跟踪实体状态(Added/Modified/Deleted),修改后自动同步到数据库;
状态管理:用Entry手动修改实体状态(如dbContext.Entry(user).State = EntityState.Modified)。
3.3 常见查询模式

模式 用途 示例
延迟加载 按需加载导航属性(需安装Proxies包) user.Orders自动触发查询
投影查询 只返回需要的字段(减少数据传输) Select(u => new { u.Name, u.Email })
分组查询 按字段分组统计 GroupBy(u => u.Name).Select(g => new { Name = g.Key, Count = g.Count() })

3.4 迁移(Migration)
EF Core用迁移管理数据库schema变化:
1.创建迁移:dotnet ef migrations add InitialCreate;
2.应用迁移:dotnet ef database update;
3.回滚迁移:dotnet ef database update 。
4. 常见问题与解决
4.1 实体无主键
错误:无法为实体类型'User'找到合适的主键;
解决:添加Id字段或用[Key]标记主键。
4.2 N+1查询问题
症状:查询100个用户,生成101个SQL(1个查用户+100个查订单);
解决:用Include(u => u.Orders)立即加载导航属性。
4.3 循环引用序列化
症状:序列化User时,Orders→User→Orders无限循环;
解决:关闭延迟加载,或配置序列化工具忽略循环引用(如Newtonsoft.Json的ReferenceLoopHandling.Ignore)。
4.4 连接字符串硬编码
解决:生产环境用配置文件(如appsettings.json)存储连接字符串,通过IConfiguration读取。
5. 最佳实践
1.DbContext依赖注入:ASP.NET Core中用AddDbContext注册Scoped生命周期的DbContext;
2.优先Fluent API:复杂映射用Fluent API,代码更易维护;
3.批量操作优化:用AddRange/RemoveRange代替循环Add/Remove;
4.避免长期持有DbContext:每次操作创建新实例,防止内存泄漏;
5.** 用迁移管理schema**:禁止手动修改数据库,所有变化通过迁移实现。
总结
EF Core是.NET开发者必备的ORM工具,核心价值在于简化数据库操作与提高开发效率。本节覆盖了映射、查询、优化的基础用法,后续可深入学习性能调优、多租户、读写分离等高级特性。掌握EF Core,你将告别手写SQL的繁琐,专注于业务逻辑实现。
(本节完)
下一章:EF Core高级查询与性能优化
(深入讲解复杂查询、索引优化、批量操作)

本站原创,转载请注明出处:


相关教程