1.添加连接字符串在(appsettings.json)中
cs
{
//数据库连接信息
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=water;Trusted_Connection=True;TrustServerCertificate=True;"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
2.创建实体类
数据类型对应和导航属性设置
|---------------|------------|------------------------|
| SQL Server 类型 | EF Core 类型 | 示例字段 |
| bigint | long | Id 、 UserId 等 |
| varchar(n) | string | Name 、 Phone 等 |
| datetime | DateTime | CreateTime 、 OrderTime |
| decimal(p,s) | decimal | Price 、 Amount 等 |
| tinyint | byte | IsDefault 、 PayStatus |
| int | int | Status 、 Number 等 |
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Water.Infrastructure.Data.Entities
{
public class WaterInfo
{
public long Id { get; set; } // bigint
public string Name { get; set; } // varchar(32)
public long CategoryId { get; set; } // 外键
public decimal Price { get; set; } // decimal(10,2)
public string Image { get; set; } // varchar(255)
public string Description { get; set; } // varchar(255)(可空)
public int? Status { get; set; } // int(可空)
public DateTime CreateTime { get; set; } // datetime
public DateTime UpdateTime { get; set; } // datetime
// 导航属性:关联Category、WaterSpec、OrderDetail、ShoppingCart
public Category Category { get; set; }
public ICollection<WaterSpec> WaterSpecs { get; set; }
public ICollection<OrderDetail> OrderDetails { get; set; }
public ICollection<ShoppingCart> ShoppingCarts { get; set; }
}
}
3.配置数据库上下文
cs
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Water.Infrastructure.Data.Entities;
namespace Water.Infrastructure.Data
{
public class WaterDbContext:DbContext
{
public WaterDbContext(DbContextOptions<WaterDbContext> options) : base(options)
{
}
// 定义DbSet,对应数据库表
public DbSet<AddressBook> AddressBooks { get; set; }
public DbSet<Category> Categorys { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<OrderDetail> OrderDetails { get; set; }
public DbSet<ShoppingCart> ShoppingCarts { get; set; }
public DbSet<WaterInfo> WaterInfos { get; set; }
public DbSet<WaterSpec> WaterSpecs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);//调用父类的默认配置逻辑,确保 EF Core 的默认约定(如主键自动识别)仍生效
// 配置AddressBook表
modelBuilder.Entity<AddressBook>(entity =>
{
entity.ToTable("AddressBook"); // 1. 指定映射到数据库的表名为"AddressBook"
entity.HasKey(e => e.Id); // 2. 设值Id为主键
entity.Property(e => e.Id).ValueGeneratedOnAdd(); // 3. 主键自增(插入时自动生成值)
// 4. 配置字段约束:限制长度+非空
entity.Property(e => e.Consignee).HasMaxLength(50).IsRequired(); // 收件人姓名:最长50字符,非空
entity.Property(e => e.Phone).HasMaxLength(11).IsRequired(); // 电话:最长11字符(手机号),非空
entity.Property(e => e.ProvinceName).HasMaxLength(32).IsRequired(); // 省份:最长32字符,非空
entity.Property(e => e.CityName).HasMaxLength(32).IsRequired(); // 城市:同上
entity.Property(e => e.DistrictName).HasMaxLength(32).IsRequired(); // 区县:同上
entity.Property(e => e.Detail).HasMaxLength(200).IsRequired(); // 详细地址:最长200字符,非空
// 5. 配置与User表的关联关系(外键+级联删除)
entity.HasOne(d => d.User) // 每个AddressBook属于一个User(多对一)
.WithMany(p => p.AddressBooks) // 一个User可以有多个AddressBook(一对多)
.HasForeignKey(d => d.UserId) // 外键字段是AddressBook的UserId
.OnDelete(DeleteBehavior.Cascade); // 级联删除:当User被删除时,关联的AddressBook也自动删除
});
// 配置Category表
modelBuilder.Entity<Category>(entity =>
{
entity.ToTable("Category");
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).ValueGeneratedOnAdd();
entity.Property(e => e.Name).HasMaxLength(32).IsRequired();
});
// 配置Order表
modelBuilder.Entity<Order>(entity =>
{
entity.ToTable("Order");
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).ValueGeneratedOnAdd();
entity.Property(e => e.Number).HasMaxLength(50).IsRequired();
entity.Property(e => e.Phone).HasMaxLength(11).IsRequired();
entity.Property(e => e.Address).HasMaxLength(200).IsRequired();
entity.Property(e => e.UserName).HasMaxLength(32).IsRequired();
entity.Property(e => e.Consignee).HasMaxLength(32).IsRequired();
entity.HasOne(d => d.User)
.WithMany(p => p.Orders)
.HasForeignKey(d => d.UserId)
.OnDelete(DeleteBehavior.Cascade);
entity.HasOne(d => d.AddressBook)
.WithMany()
.HasForeignKey(d => d.AddressBookId)
.OnDelete(DeleteBehavior.Cascade);
});
// 配置OrderDetail表
modelBuilder.Entity<OrderDetail>(entity =>
{
entity.ToTable("OrderDetail");
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).ValueGeneratedOnAdd();
entity.Property(e => e.Name).HasMaxLength(32).IsRequired();
entity.Property(e => e.Image).HasMaxLength(255).IsRequired();
entity.HasOne(d => d.Order)
.WithMany(p => p.OrderDetails)
.HasForeignKey(d => d.OrderId)
.OnDelete(DeleteBehavior.Cascade);
entity.HasOne(d => d.WaterInfo)
.WithMany(p => p.OrderDetails)
.HasForeignKey(d => d.WaterInfoId)
.OnDelete(DeleteBehavior.Cascade);
entity.HasOne(d => d.WaterSpec)
.WithMany(p => p.OrderDetails)
.HasForeignKey(d => d.WaterSpecId)
.OnDelete(DeleteBehavior.Cascade);
});
// 配置ShoppingCart表
modelBuilder.Entity<ShoppingCart>(entity =>
{
entity.ToTable("ShoppingCart");
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).ValueGeneratedOnAdd();
entity.Property(e => e.Name).HasMaxLength(32).IsRequired();
entity.Property(e => e.Image).HasMaxLength(255).IsRequired();
entity.HasOne(d => d.User)
.WithMany(p => p.ShoppingCarts)
.HasForeignKey(d => d.UserId)
.OnDelete(DeleteBehavior.Cascade);
entity.HasOne(d => d.WaterInfo)
.WithMany(p => p.ShoppingCarts)
.HasForeignKey(d => d.WaterInfoId)
.OnDelete(DeleteBehavior.Cascade);
entity.HasOne(d => d.WaterSpec)
.WithMany(p => p.ShoppingCarts)
.HasForeignKey(d => d.WaterSpecId)
.OnDelete(DeleteBehavior.Cascade);
});
// 配置User表
modelBuilder.Entity<User>(entity =>
{
entity.ToTable("User");
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).ValueGeneratedOnAdd();
entity.Property(e => e.OpenId).HasMaxLength(45).IsRequired();
entity.Property(e => e.Name).HasMaxLength(32).IsRequired();
entity.Property(e => e.Phone).HasMaxLength(11).IsRequired();
});
// 配置WaterInfo表
modelBuilder.Entity<WaterInfo>(entity =>
{
entity.ToTable("WaterInfo");
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).ValueGeneratedOnAdd();
entity.Property(e => e.Name).HasMaxLength(32).IsRequired();
entity.Property(e => e.Image).HasMaxLength(255).IsRequired();
entity.HasOne(d => d.Category)
.WithMany(p => p.WaterInfos)
.HasForeignKey(d => d.CategoryId)
.OnDelete(DeleteBehavior.Cascade);
});
// 配置WaterSpec表
modelBuilder.Entity<WaterSpec>(entity =>
{
entity.ToTable("WaterSpec");
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).ValueGeneratedOnAdd();
entity.Property(e => e.SpecName).HasMaxLength(32).IsRequired();
entity.HasOne(d => d.WaterInfo)
.WithMany(p => p.WaterSpecs)
.HasForeignKey(d => d.WaterId)
.OnDelete(DeleteBehavior.Cascade);
});
}
}
}
3.1.Fluent API配置
1. 哪些情况可以依赖默认约定(不用显式配置)?
EF Core 的 "约定优于配置" 原则会自动处理一些基础映射,比如:
- 主键:实体中名为Id或 "类名 + Id" 的属性(如AddressBookId)会被自动识别为主键,且默认是自增的(ValueGeneratedOnAdd)。
- 表名:默认用实体类名的复数形式(如AddressBook实体→AddressBooks表)。
- 外键:如果实体有导航属性(如AddressBook中的User User),且存在UserId属性,EF 会自动推断UserId是关联User表的外键。
- 数据类型:基础类型(如string→nvarchar(max)、int→int、decimal→decimal(18,2))会自动映射,无需额外配置。
2. 哪些情况必须 / 建议显式配置?
当默认约定不符合需求时,必须显式配置(用 Fluent API 或特性):
(1)主键非默认命名如果主键不叫
Id或 "类名 + Id",比如AddressBook的主键是BookId,则必须显式配置主键:
cs
modelBuilder.Entity<AddressBook>().HasKey(e => e.BookId);
(2)表名 / 列名自定义如果表名不想用复数
(如AddressBook实体对应表AddressBook而非AddressBooks),或列名与属性名不同(如属性Consignee对应列Receiver):
cs
// 表名
modelBuilder.Entity<AddressBook>().ToTable("AddressBook");
// 列名
modelBuilder.Entity<AddressBook>().Property(e => e.Consignee).HasColumnName("Receiver");
(3)字段长度 / 约束
string类型默认映射为nvarchar(max),但实际业务中通常需要限制长度(如varchar(50)),否则可能导致数据库性能问题或存储空间浪费:
cs
// 限制长度为50,且非空
modelBuilder.Entity<AddressBook>().Property(e => e.Consignee).HasMaxLength(50).IsRequired();
(4)外键关系复杂比如多对多关系、自引用关系,或外键命名不符合默认约定
(如AddressBook中关联User的外键是UId而非UserId):
cs
// 显式指定外键
modelBuilder.Entity<AddressBook>()
.HasOne(d => d.User)
.WithMany(p => p.AddressBooks)
.HasForeignKey(d => d.UId); // 外键是UId而非默认的UserId
(5)数据类型精确控制默认映射可能不符合数据库设计
(如decimal默认是decimal(18,2),但业务需要decimal(10,2);string需要映射为varchar而非默认的nvarchar):
cs
// 精确指定decimal精度
modelBuilder.Entity<Order>().Property(e => e.Amount).HasPrecision(10, 2);
// 映射为varchar(默认是nvarchar)
modelBuilder.Entity<User>().Property(e => e.Phone).IsUnicode(false); // IsUnicode(false)对应varchar
(6)索引、默认值、检查约束等高级配置如果需要给字段加索引
(如User表的Phone字段需要唯一索引),或设置默认值(如CreateTime默认是当前时间):
cs
// 唯一索引
modelBuilder.Entity<User>().HasIndex(e => e.Phone).IsUnique();
// 默认值
modelBuilder.Entity<User>().Property(e => e.CreateTime).HasDefaultValueSql("GETDATE()");
4.在program.cs中注册数据库上下文
cs
var builder = WebApplication.CreateBuilder(args);
//注册ef core上下文,关联sql server连接字符串
builder.Services.AddDbContext<WaterDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));