.Net C# 基于EFCore的DBFirst和CodeFirst
DBFirst和CodeFirst
1 概念介绍
1.1 DBFirst(数据库优先)
- 含义:这种模式是先创建数据库架构,包括表、视图、存储过程等数据库对象。然后通过实体框架(Entity Framework)等工具,根据已有的数据库结构来生成对应的实体类和数据访问代码。
- 应用场景:当已经存在一个成熟的数据库,例如从旧系统迁移过来的数据库,或者数据库由专业的数据库管理员设计好,开发人员需要基于这个数据库来构建应用程序时,DBFirst 是一个很好的选择。
1.2 CodeFirst(代码优先)
- 含义:开发人员首先通过编写代码来定义实体类及其之间的关系,如在 C# 中使用实体框架的 CodeFirst 模式。然后,根据这些代码中的定义,实体框架可以自动创建数据库架构或者更新已有的数据库架构来匹配代码中的实体定义。
- 应用场景:在新项目的开发初期,尤其是团队中有熟练的开发人员能够很好地设计领域模型时,CodeFirst 可以让开发人员专注于业务逻辑和实体关系的构建,而不用担心数据库的细节。它非常适合敏捷开发环境,能够快速迭代数据库架构和应用程序代码。
2 工作流程对比
2.1 DBFirst 工作流程
- 数据库设计:数据库管理员(DBA)或者熟悉数据库设计的人员使用数据库管理工具(如 SQL Server Management Studio 等)创建数据库,包括表结构、数据类型、约束条件等。
- 生成代码:在 Visual Studio 等开发工具中,使用实体框架的 “从数据库生成模型” 功能,根据数据库结构生成实体类和数据访问上下文(DbContext)类。这些生成的类可以作为数据访问层的基础,开发人员可以在其上添加业务逻辑。
- 数据访问和业务逻辑:开发人员在生成的代码基础上,编写业务逻辑方法,如查询、插入、更新和删除数据等操作,将数据访问和业务逻辑结合起来,实现应用程序的功能。
2.2 CodeFirst 工作流程
- 实体类定义:开发人员在代码中定义实体类,包括属性、数据类型和实体之间的关系(如一对多、多对多等)。例如,在 C# 中使用实体框架时,通过使用System.ComponentModel.DataAnnotations命名空间中的特性(如[Key]用于定义主键,[Required]用于定义必填字段等)来装饰实体类的属性。
- 数据库上下文定义:创建一个继承自DbContext的类,在这个类中定义DbSet属性,用于表示数据库中的表。例如,public DbSet Customers { get; set; }表示一个名为Customers的表,其对应的实体类是Customer。
- 数据库迁移(可选但推荐):使用实体框架的迁移功能(如Enable - Migrations和Add - Migration命令)来创建和管理数据库架构的变更。在应用程序启动时,通过调用Database.SetInitializer方法或者在配置文件中设置数据库初始化策略,实体框架可以根据实体类的定义自动创建或者更新数据库。
- 数据访问和业务逻辑:与 DBFirst 类似,开发人员在定义好的实体类和数据库上下文基础上,编写业务逻辑方法来实现应用程序的功能。
3 优缺点对比
3.1 DBFirst 优缺点
- 优点:
- 对于已有的复杂数据库,能够快速生成对应的代码,节省了大量的实体类和数据访问代码的编写时间。
- 数据库架构已经确定,开发人员只需要关注应用程序如何使用数据库中的数据,适合数据库结构相对稳定的项目。
- 缺点:
- 如果数据库结构发生变化,需要重新生成代码,可能会导致之前对生成代码的修改丢失,需要重新进行合并和调整。
- 开发人员对数据库结构的依赖较强,在代码中对数据库的更改可能不够灵活,需要在数据库和代码之间频繁切换来进行修改。
3.2 CodeFirst 优缺点
- 优点:
- 开发人员可以完全通过代码来控制数据库架构,使得领域模型和代码之间的一致性更好。
- 便于进行敏捷开发和迭代,当业务需求发生变化时,可以直接在代码中修改实体类的定义,然后通过迁移来更新数据库架构。
- 缺点:
- 对于大型的、已有的数据库迁移到 CodeFirst 模式可能比较复杂,需要花费更多的时间来重新设计和实现实体类以及数据访问逻辑。
- 开发人员需要对实体框架的 CodeFirst 相关知识有深入的了解,如数据库迁移策略、数据注释和流畅 API 等,否则可能会导致数据库架构不符合预期或者出现数据丢失等问题。
4 CoreFirst 示例
4.1 环境搭建
- 创建项目
- 创建一个新的.NET 项目(如控制台应用程序或ASP.NET Core Web API 等)。
- 安装必要的 NuGet 包
- 安装Npgsql.EntityFrameworkCore.PostgreSQL包。这个包允许你在 Entity Framework Core 中使用 PostgreSQL 数据库。可以通过在项目目录下的命令行中执行dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL来安装。
4.2 定义实体类
-
Blog 类
using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace BloggingApp { [Table("blogs")] public class Blog { [Key] public int BlogId { get; set; } [Required] [StringLength(100)] public string Name { get; set; } public DateTime CreatedDate { get; set; } = DateTime.Now; public IList<Post> Posts { get; set; } = new List<Post>(); } }
- 解释:
[Table("blogs")]
指定实体类对应的数据库表名为blogs
。[Key]
定义BlogId
为表的主键。[Required]
和[StringLength(100)]
用于验证Name
属性,确保其为必填且长度不超过 100。CreatedDate
默认为当前日期时间,表示博客的创建时间。Posts
是一个IList<Post>
类型的属性,表示一篇博客可以有多篇文章(Post
)与之关联。
- 解释:
-
Post 类
[Table("posts")] public class Post { [Key] public int PostId { get; set; } [Required] [StringLength(200)] public string Title { get; set; } public string Content { get; set; } public DateTime PublishedDate { get; set; } = DateTime.Now; public int BlogId { get; set; } [ForeignKey("BlogId")] public Blog Blog { get; set; } public IList<Comment> Comments { get; set; } = new List<Comment>(); }
- 解释:
[Table("posts")]
表示对应的数据库表是posts
。[Key]
定义PostId
为主键。Title
是文章标题,Content
是文章内容,PublishedDate
默认为当前时间,表示发布日期。BlogId
是外键,通过[ForeignKey("BlogId")]
特性关联到Blog
类的BlogId
,Blog
属性表示这篇文章所属的博客。Comments
是一个IList<Comment>
类型的属性,表示一篇文章可以有多个评论(Comment
)。
- 解释:
-
Comment 类
[Table("comments")] public class Comment { [Key] public int CommentId { get; set; } [Required] public string Content { get; set; } public DateTime CommentedDate { get; set; } = DateTime.Now; public int PostId { get; set; } [ForeignKey("PostId")] public Post Post { get; set; } }
- 解释:
[Table("comments")]
表示对应的数据库表是comments
。[Key]
定义CommentId
为主键。Content
是评论内容,CommentedDate
默认为当前时间,表示评论日期。PostId
是外键,通过[ForeignKey("PostId")]
特性关联到Post
类的PostId
,Post
属性表示这个评论所属的文章。
- 解释:
4.3 创建数据库上下文类
using Microsoft.EntityFrameworkCore;
namespace BloggingApp
{
public class BloggingContext : DbContext
{
public BloggingContext(DbContextOptions<BloggingContext> options) : base(options)
{
}
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<Comment> Comments { get; set; }
}
}
- 解释:
- 构造函数接受DbContextOptions<BloggingContext>
类型的参数,并传递给基类DbContext
的构造函数。
-DbSet<Blog> Blogs
、DbSet<Post> Posts
和DbSet<Comment> Comments
分别定义了对应数据库表的属性,用于操作blogs
、posts
和comments
表。
4.4 配置数据库连接字符串
- 在
appsettings.json
文件中添加以下内容:
{
"ConnectionStrings": {
"BloggingDbConnection": "Host=localhost;Database=blogging_app;Username=postgres;Password=your_password"
}
}
-
注意:
- 你需要将
your_password
替换为你实际的 PostgreSQL 数据库密码。 Host
是数据库服务器地址,Database
是数据库名称,Username
是登录用户名。
- 你需要将
-
在
Program.cs
(如果是控制台应用程序)或Startup.cs
(如果是ASP.NET Core 应用程序)中读取连接字符串并配置数据库上下文:- 对于控制台应用程序,在
Program.cs
中:
using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using System.IO; class Program { static void Main() { var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); IConfigurationRoot configuration = builder.Build(); var connectionString = configuration.GetConnectionString("BloggingDbConnection"); var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>() .UseNpgsql(connectionString); using (var context = new BloggingContext(optionsBuilder.Options)) { // 在这里可以进行数据库操作,如插入、查询等 } } }
- 对于ASP.NET Core 应用程序,在
Startup.cs
中:
using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(ServiceCollection services) { var connectionString = Configuration.GetConnectionString("BloggingDbConnection"); services.AddDbContext<BloggingContext>(options => options.UseNpgsql(connectionString)); // 其他服务配置 } // 其他方法 }
- 对于控制台应用程序,在
4.5 数据库迁移和操作
-
安装EF工具
- 安装全局工具
dotnet tool install --global dotnet-ef
- 更新工具
dotnet tool update --global dotnet-ef
- 安装全局工具
-
添加迁移(假设是初始创建)
- 在命令行中执行
dotnet ef migrations add InitialCreate
。
- 在命令行中执行
-
更新数据库
- 在命令行中执行
dotnet ef database update
。
- 在命令行中执行
-
数据库操作示例 - 插入数据
- 在
using
块中(对于控制台应用程序)或在服务方法中(对于ASP.NET Core 应用程序)插入数据:
using (var context = new BloggingContext(optionsBuilder.Options)) { var blog = new Blog { Name = "My Awesome Blog" }; var post = new Post { Title = "First Post", Content = "This is the content of my first post.", Blog = blog }; var comment = new Comment { Content = "Great post!", Post = post }; context.Blogs.Add(blog); context.Posts.Add(post); context.Comments.Add(comment); context.SaveChanges(); }
- 在
-
解释:
- 首先创建一个
Blog
对象,然后创建一个Post
对象并关联到前面的Blog
,接着创建一个Comment
对象并关联到Post
。 - 通过
context.Blogs.Add
、context.Posts.Add
和context.Comments.Add
将这些对象添加到相应的数据库表中。 - 最后,通过
context.SaveChanges
将更改保存到数据库。
- 首先创建一个
5 DBFirst 示例
5.1 生成数据库上下文和实体类
- 打开命令行工具,执行
dotnet ef dbcontext scaffold "Host=localhost;Port=5432;Database=your_database_name;Username=postgres;Password=your_password" Npgsql.EntityFrameworkCore.PostgreSQL -o Models
。- 解释:
- 这个命令根据指定的 PostgreSQL 数据库连接字符串和提供程序(
Npgsql.EntityFrameworkCore.PostgreSQL
)来生成数据库上下文和实体类,并输出到Models
文件夹下。
- 这个命令根据指定的 PostgreSQL 数据库连接字符串和提供程序(
- 解释: