最近终于闲下来了,有点时间来写一下使用efcore过程中遇到的的一些细节问题。这个系列会从基础开始,可能会比较啰嗦(~ ̄▽ ̄)~。
开发环境 VS2019 .NET Core2.2 mysql8.0
会用到的包,列一下:
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Tools
Pomelo.EntityFrameworkCore.MySql
Microsoft.Extensions.Configuration
Microsoft.Extensions.Configuration.Json
为什么不用官方的包MySql.Data.EntityFrameworkCore,我只能说,从2018年4月份开始使用corefirst的体验来看,官方的驱动做的实在是烂,对数据迁移不够友好,至2019年群里的小伙伴还在吐槽不支持GUID(未核实)。
接下来是正文:
1. 新建一个控制台项目EFCoreFirst,做一下简单的演示就不用web项目了。
添加一个appsetting.json配置文件,属性设置始终复制
{
"ConnectionString": "server=127.0.0.1;port=3306;database=efcore_demo;uid=sa;pwd=123456;"
}
2. 安装包
3. 添加实体
ef core是关于访问数据库的,但是这个数据库是从哪里来的呢?ef core提供了两个选项:通过ef core创建它,称为code first,或者手动创建数据库,称为database first。
这个例子中会使用codefirst创建一个简单的数据库,包含两张表:Products 包含产品信息,Order 包含订单信息
现在可以开始编写efcore相关的代码了,在创建任何数据库访问代码之前,需要编写两个基本部分:
3.1 由ef core映射到数据库中表的类
3.2 应用程序的DbContext,用于配置和访问数据库的类
products类:
[Table("product")]
public class Products
{
/// <summary>
/// key显示将ProductId设为主键,但是我们已经使用了efcore命名约定,它告诉efcore属性ProductId是主键,所以可以省略
/// </summary>
[Key]
public virtual int ProductId { get; set; }
/// <summary>
/// 产品名称
/// </summary>
[StringLength(50)]
public virtual string Name { get; set; }
/// <summary>
/// 产品数量
/// </summary>
public virtual int Count { get; set; }
/// <summary>
/// 价格
/// </summary>
public virtual Decimal Price { get; set; }
/// <summary>
/// 外键
/// </summary>
public virtual int OrderId { get; set; }
/// <summary>
/// Order属性是efcore导航属性。efcore在save中使用这个来查看products否附加了order类,如果附加了order类,它将设置外键orderId
/// 这里有一个新的命名约定 efcore知道OrderId是一个外键,当它与Order类的主键名称一致
/// </summary>
public virtual Order Order { get; set; }
}
Order类:
[Table("order")]
public class Order
{
[Key]
public virtual int OrderId { get; set; }
/// <summary>
/// 订单编号
/// </summary>
[StringLength(25)]
public virtual string OrderCode { get; set; }
/// <summary>
/// 买家名称
/// </summary>
[StringLength(25)]
public virtual string Buyer { get; set; }
}
4. 添加数据库上下文
public class AppDbContext:DbContext
{
private static string ConnStr;
static AppDbContext()
{
var builder = new ConfigurationBuilder();
builder.AddJsonFile("appsetting.json");
var config = builder.Build();
ConnStr = config["ConnectionString"];
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseMySql(ConnStr);
}
public DbSet<Products> Products { get; set; }
}
5. 添加迁移文件,执行迁移
查看生成的数据库表,可以看到表product表按预期生成
efcore生成迁移文件的过程,以products为例子:
5.1 在AppDbContext中查找所有DbSet<T> 属性,这里找到了Products类,获取到了表名称(从Table特性中,未设置则默认为类名)
5.2 查看DbSet<T>中所有类T(这里是Products类),并查看其属性以计算列名、类型等。它还查找类和或属性上提供额外建模信息的特殊属性如StringLength特性
5.3 查找DbSet<T>类所引用的任何类(Products类中的Order)。在这个例子中,Products类引用了Order类,所以efcore也会扫描它。它对Order类的属性执行与步骤2中对Products相同的操作。
5.4 efcore在应用程序的DbContext中运行OnModelCreating方法,这里我们未重写这个方法,该方法可以添加一些配置。
5.5 efcore根据以上步骤收集的所有信息创建数据库的内部模型,生成迁移文件。
6. 尝试操作数据
public class Program
{
static void Main(string[] args)
{
AddProduct();
var product = GetSingleProduct();
Console.WriteLine($"{product?.ProductId}--{product?.Name}");
Console.ReadLine();
}
public static void AddProduct()
{
using (var dbContext=new AppDbContext())
{
var product = new Products()
{
Name = "apple",
Price = 3.0M,
Count = 10,
Order = new Order {
OrderCode = "0001",
Buyer="xiaoming"
}
};
dbContext.Add<Products>(product);
dbContext.SaveChanges();
}
}
public static Products GetSingleProduct()
{
using (var dbContext = new AppDbContext())
{
var product = dbContext.Products.FirstOrDefault();
return product;
}
}
}
操作成功: