在 ASP.NET Core Web API 中使用 Entity Framework Core 进行 CRUD 操作






4.80/5 (16投票s)
本文将解释如何在 ASP.NET Core Web API 中使用 Entity Framework Core 执行 CRUD(创建、读取、更新和删除)操作。
引言
本文将解释如何在 ASP.NET Core Web API 中使用 Entity Framework Core 执行 CRUD(创建、读取、更新和删除) 操作。我们将逐步了解 ASP.NET Core Web API 中的 CRUD 操作。在此演示中,我们将使用 Database First 方法,即在创建实际代码之前,我们的数据库将准备就绪。
让我们开始这个演示并创建 CRUD 操作。但在进行 ASP.NET Core Web API 中 CRUD 操作的实际演示之前。让我们了解这个演示的目标,它将说明本文将涵盖哪些内容。以下是目标:
- 创建数据库和表
- 创建 ASP.NET Core Web API 项目
- 安装 Entity Framework 的 Nuget 包
- 生成模型类
- 设置依赖注入
- 创建存储库并实现 CRUD 操作
- 创建控制器并创建 API 调用
- 在 Postman 中测试 API 端点
- 启用 CORS
创建数据库和表
首先,我们将打开 SQL Server Management Studio 并创建一个名为 "BlogDB
" 的数据库,以及两个表:"Category
" 和 "Post
"。Category
表将保存博客文章类别的信息,而 Post
表将实际保存文章的详细信息。因此,您可以使用以下 SQL 代码片段创建数据库和表。
USE BlogDB
GO
CREATE TABLE Category(ID INT PRIMARY KEY IDENTITY(1,1), NAME VARCHAR(255), SLUG VARCHAR(255));
INSERT INTO Category(NAME, SLUG) VALUES('CSHARP', 'csharp');
INSERT INTO Category(NAME, SLUG) VALUES('VISUAL STUDIO', 'visualstudio');
INSERT INTO Category(NAME, SLUG) VALUES('ASP.NET CORE', 'aspnetcore');
INSERT INTO Category(NAME, SLUG) VALUES('SQL SERVER','sqlserver');
CREATE TABLE Post(POST_ID INT PRIMARY KEY IDENTITY(1,1), TITLE VARCHAR(2000),
DESCRIPTION VARCHAR(MAX), CATEGORY_ID INT FOREIGN KEY REFERENCES Category(ID),
CREATED_DATE DATETIME);
创建 ASP.NET Core Web API 项目
现在,我们已经准备好数据库和表。因此,让我们开始创建 ASP.NET Core Web API 项目,但请确保您的开发环境已为 .NET Core 应用程序做好准备。您需要一些先决条件才能开始使用 Visual Studio 2017 进行此演示。首先,您应该已安装 .NET Core 2.0.0 或更高版本的 SDK,其次,您应该已安装最新版本的 Visual Studio 2017,即 VS 2017 15.7+ 版本。一旦您的环境准备就绪,您就可以开始了。在创建新的 ASP.NET Core Web API 时,我们应该遵循以下步骤。
- 打开 Visual Studio 2017
- 从菜单中点击 文件 > 新建 > 项目
- 在“新建项目”窗口中,从左侧面板中选择 已安装 > Visual C# > Web
- 从中间面板中选择 ASP.NET Core Web 应用程序项目模板
- 输入 CoreServices 作为项目名称并点击 确定
- 将出现下一个 新建 ASP.NET Core Web 应用程序 对话框。
- 选择 目标框架 为 .NET Core,并从下拉菜单中选择版本为 ASP.NET Core 2.1
- 选择 API 作为模板
- 选择身份验证为 “无身份验证”
- 单击确定
由于我们在创建项目时选择了“配置 HTTPS”,因此会要求为该项目配置 SSL。要进行配置,请单击 是。
如果我们正确完成每个步骤,我们的项目将在几秒钟内准备就绪,并且看起来像这样。在这里,您可以通过以下图像查看 ASP.NET Core Web API 的项目结构。尽管所有组件对于此应用程序都很重要,但我们主要应关注 Startup.cs、Appsettings.json、Dependencies 和 API Controller。
安装 Entity Framework 的 Nuget 包
我们已经知道,我们将使用 Entity Framework Core 作为此演示的 ORM。要使用 EF Core,我们必须安装一些包,这些包将提供与 EF Core 配合使用的类。我们可以使用 Package Manager Console 执行命令来安装特定包,或者直接在 NuGet Package Manager 中搜索并安装它。因此,让我们逐个安装此解决方案所需的包。右键单击项目并选择 管理 NuGet 包。它将打开 Nuget 包管理器,我们可以在其中搜索任何所需的 ASP.NET Core 包并安装特定版本。
第一个是 Microsoft.EntityFrameworkCore.SqlServer
,它将提供连接 SQL Server 以便 Entity Framework Core 进行 CRUD 操作的类。
下一个是 Microsoft.EntityFrameworkCore.SqlServer.Design
,它将为 Entity Framework Core 的 SQL Server 提供设计时核心功能。
最后一个是 Microsoft.EntityFrameworkCore.Tools
,它将帮助处理与数据库相关的活动,例如添加迁移、脚本迁移、获取 dbcontext、更新数据库等。
生成模型类
如上所述,我们已经安装了与 Entity Framework Core 配合所需的所有必需包。因此,由于数据库已存在,我们将从数据库生成 Entity Framework Core 模型类;我们应该从包管理器控制台运行以下命令。要打开它,请转到 工具 > NuGet 包管理器 > 包管理器控制台 并执行以下命令。
Scaffold-DbContext "Server=DESKTOP-XYZ;Database=BlogDB;UID=sa;PWD=££££££££;"
Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models
通过上述命令,我们提供了 SQL Server 的详细信息,例如服务器名称、用户名和密码,以访问该服务器和包含表的现有数据库。除此之外,我们还提供了应生成 Models
类的 Output 目录。上述命令在 Model 文件夹中创建了三个类:BlogDBContext
,它就是应用程序的 DbContext
类,以及另外两个类是与数据库表相关的 Model
类。
目前,请注释掉 BlogDBContext
类的 OnConfiguring()
方法,因为稍后我们将在 Startup.cs 类中配置我们的依赖注入。
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
namespace CoreServices.Models
{
public partial class BlogDBContext : DbContext
{
public BlogDBContext()
{
}
public BlogDBContext(DbContextOptions<BlogDBContext> options)
: base(options)
{
}
public virtual DbSet<Category> Category { get; set; }
public virtual DbSet<Post> Post { get; set; }
// protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
// {
// if (!optionsBuilder.IsConfigured)
// {
//#warning To protect potentially sensitive information in your connection string,
//you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263
//for guidance on storing connection strings.
// optionsBuilder.UseSqlServer("Server=DESKTOP-XYZ;Database=BlogDB;
// UID=sa;PWD=££££££;");
// }
// }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Category>(entity =>
{
entity.Property(e => e.Id).HasColumnName("ID");
entity.Property(e => e.Name)
.HasColumnName("NAME")
.HasMaxLength(255)
.IsUnicode(false);
entity.Property(e => e.Slug)
.HasColumnName("SLUG")
.HasMaxLength(255)
.IsUnicode(false);
});
modelBuilder.Entity<Post>(entity =>
{
entity.Property(e => e.PostId).HasColumnName("POST_ID");
entity.Property(e => e.CategoryId).HasColumnName("CATEGORY_ID");
entity.Property(e => e.CreatedDate)
.HasColumnName("CREATED_DATE")
.HasColumnType("datetime");
entity.Property(e => e.Description)
.HasColumnName("DESCRIPTION")
.IsUnicode(false);
entity.Property(e => e.Title)
.HasColumnName("TITLE")
.HasMaxLength(2000)
.IsUnicode(false);
entity.HasOne(d => d.Category)
.WithMany(p => p.Post)
.HasForeignKey(d => d.CategoryId)
.HasConstraintName("FK__Post__CATEGORY_I__1273C1CD");
});
}
}
}
这是一个从数据库自动生成的 Category Model
类。
using System;
using System.Collections.Generic;
namespace CoreServices.Models
{
public partial class Category
{
public Category()
{
Post = new HashSet<Post>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Slug { get; set; }
public ICollection<Post> Post { get; set; }
}
}
这是一个从数据库自动生成的 Post 模型类。
using System;
using System.Collections.Generic;
namespace CoreServices.Models
{
public partial class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public int? CategoryId { get; set; }
public DateTime? CreatedDate { get; set; }
public Category Category { get; set; }
}
}
由于我们已注释掉 BlogDBContext
的 OnConfiguring()
方法,现在我们将创建访问数据库并执行 CRUD 操作的机制。首先,我们将在 appsettings.json 中定义我们的 connection string
,如下所示:
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"ConnectionStrings": {
"BlogDBConnection": "Server=DESKTOP-XYZ;Database=BlogDB;UID=sa;PWD=#########;"
},
"AllowedHosts": "*"
}
设置依赖注入
现在,让我们创建一个 Repository
类作为此 API 的中间层。因此,只需创建一个名为 'Repository' 的文件夹,并创建两个文件,一个名为 'IPostRepository',另一个名为 'PostRepository'。目前,暂时保留这些接口和类。我们稍后将实现 CRUD 代码。现在,直接跳转到 Startup.cs 类,并使用以下代码为 BlogDBContext
类和 PostRepository
添加依赖注入:
services.AddDbContext<BlogDBContext>(item => item.UseSqlServer
(Configuration.GetConnectionString("BlogDBConnection")));
services.AddScoped<IPostRepository, PostRepository>();
Startup.cs 类的完整代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CoreServices.Models;
using CoreServices.Repository;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace CoreServices
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddDbContext<BlogDBContext>(item => item.UseSqlServer
(Configuration.GetConnectionString("BlogDBConnection")));
services.AddScoped<IPostRepository, PostRepository>();
}
// This method gets called by the runtime. Use this method
// to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
}
}
现在,让我们创建一个名为 ViewModel 的文件夹,并创建一个名为 PostViewModel
的类。这只是一个模型类,负责从多个来源获取数据,因为我们需要同时显示类别和相关文章的数据。因此,这就是我们创建这个 PostViewModel
的原因。
using System;
namespace CoreServices.ViewModel
{
public class PostViewModel
{
public int PostId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public int? CategoryId { get; set; }
public DateTime? CreatedDate { get; set; }
public string CategoryName { get; set; }
}
}
创建存储库并实现 CRUD 操作
现在我们开始实际的 CRUD 代码实现。打开 IPostRepository
并添加 CRUD 操作所需的方法。因此,我们可以通过以下 IPostRepository 接口
看到,我们为不同的目的定义了不同的方法。GetCategories
将获取可用类别列表,GetPosts
将获取可用文章列表,GetPost
将获取特定文章 ID 的单个文章,AddPost
将添加新的文章详情,DeletePost
将根据文章 ID 删除单个文章,最后 UpdatePost
将更新现有文章。由于我们返回任务特定数据,这意味着数据将异步返回。
using CoreServices.Models;
using CoreServices.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace CoreServices.Repository
{
public interface IPostRepository
{
Task<List<Category>> GetCategories();
Task<List<PostViewModel>> GetPosts();
Task<PostViewModel> GetPost(int? postId);
Task<int> AddPost(Post post);
Task<int> DeletePost(int? postId);
Task UpdatePost(Post post);
}
}
现在,让我们转到 PostRepository
并实现 IPostRepository
。首先,我们将使用 构造函数依赖注入 获取 BlogDBContext
的实例。一旦实例可用,我们将进行进一步的实现。因此,使用以下代码实现 CRUD 操作(创建文章、读取文章、更新文章和删除文章)。在这个 PostRepository
类中,我们将使用 Entity Framework Core 与数据库交互并执行 CRUD 操作。
using CoreServices.Models;
using CoreServices.ViewModel;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace CoreServices.Repository
{
public class PostRepository : IPostRepository
{
BlogDBContext db;
public PostRepository(BlogDBContext _db)
{
db = _db;
}
public async Task<List<Category>> GetCategories()
{
if (db != null)
{
return await db.Category.ToListAsync();
}
return null;
}
public async Task<List<PostViewModel>> GetPosts()
{
if (db != null)
{
return await (from p in db.Post
from c in db.Category
where p.CategoryId == c.Id
select new PostViewModel
{
PostId = p.PostId,
Title = p.Title,
Description = p.Description,
CategoryId = p.CategoryId,
CategoryName = c.Name,
CreatedDate = p.CreatedDate
}).ToListAsync();
}
return null;
}
public async Task<PostViewModel> GetPost(int? postId)
{
if (db != null)
{
return await (from p in db.Post
from c in db.Category
where p.PostId == postId
select new PostViewModel
{
PostId = p.PostId,
Title = p.Title,
Description = p.Description,
CategoryId = p.CategoryId,
CategoryName = c.Name,
CreatedDate = p.CreatedDate
}).FirstOrDefaultAsync();
}
return null;
}
public async Task<int> AddPost(Post post)
{
if (db != null)
{
await db.Post.AddAsync(post);
await db.SaveChangesAsync();
return post.PostId;
}
return 0;
}
public async Task<int> DeletePost(int? postId)
{
int result = 0;
if (db != null)
{
//Find the post for specific post id
var post = await db.Post.FirstOrDefaultAsync(x => x.PostId == postId);
if (post != null)
{
//Delete that post
db.Post.Remove(post);
//Commit the transaction
result = await db.SaveChangesAsync();
}
return result;
}
return result;
}
public async Task UpdatePost(Post post)
{
if (db != null)
{
//Delete that post
db.Post.Update(post);
//Commit the transaction
await db.SaveChangesAsync();
}
}
}
}
创建控制器并创建 API 调用
现在,我们将使用 API 控制器进行实际实现,以公开 END-POINT。因此,首先通过右键单击 Controller 文件夹并选择 添加 > 新项 来添加一个新的 API 控制器,命名为“PostController
”。因此,我们将再次使用构造函数依赖注入获取 PostRepository
的实例,然后首先使用 GetCategories()
端点获取类别列表,同样,我们将为 CRUD 操作定义其余的端点。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CoreServices.Models;
using CoreServices.Repository;
using Microsoft.AspNetCore.Mvc;
namespace CoreServices.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class PostController : ControllerBase
{
IPostRepository postRepository;
public PostController(IPostRepository _postRepository)
{
postRepository = _postRepository;
}
[HttpGet]
[Route("GetCategories")]
public async Task<IActionResult> GetCategories()
{
try
{
var categories = await postRepository.GetCategories();
if (categories == null)
{
return NotFound();
}
return Ok(categories);
}
catch (Exception)
{
return BadRequest();
}
}
[HttpGet]
[Route("GetPosts")]
public async Task<IActionResult> GetPosts()
{
try
{
var posts = await postRepository.GetPosts();
if (posts == null)
{
return NotFound();
}
return Ok(posts);
}
catch (Exception)
{
return BadRequest();
}
}
[HttpGet]
[Route("GetPost")]
public async Task<IActionResult> GetPost(int? postId)
{
if (postId == null)
{
return BadRequest();
}
try
{
var post = await postRepository.GetPost(postId);
if (post == null)
{
return NotFound();
}
return Ok(post);
}
catch (Exception)
{
return BadRequest();
}
}
[HttpPost]
[Route("AddPost")]
public async Task<IActionResult> AddPost([FromBody]Post model)
{
if (ModelState.IsValid)
{
try
{
var postId = await postRepository.AddPost(model);
if (postId > 0)
{
return Ok(postId);
}
else
{
return NotFound();
}
}
catch (Exception)
{
return BadRequest();
}
}
return BadRequest();
}
[HttpDelete]
[Route("DeletePost")]
public async Task<IActionResult> DeletePost(int? postId)
{
int result = 0;
if (postId == null)
{
return BadRequest();
}
try
{
result = await postRepository.DeletePost(postId);
if (result == 0)
{
return NotFound();
}
return Ok();
}
catch (Exception)
{
return BadRequest();
}
}
[HttpPut]
[Route("UpdatePost")]
public async Task<IActionResult> UpdatePost([FromBody]Post model)
{
if (ModelState.IsValid)
{
try
{
await postRepository.UpdatePost(model);
return Ok();
}
catch (Exception ex)
{
if (ex.GetType().FullName ==
"Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException")
{
return NotFound();
}
return BadRequest();
}
}
return BadRequest();
}
}
}
在 Postman 中测试 API 端点
最后,我们完成了使用 Entity Framework Core 和 Repository 的 ASP.NET Core Web API CRUD 操作的实现。因此,让我们转到 Postman 并测试我们的 API 端点。首先测试 GetCategories
端点,如下所示。选择 Get
作为方法,在 URL 中传入端点,并定义一个名为 'Content-Type': 'application/json
' 的标头,然后单击“发送”按钮。它将返回可用类别列表。
接下来在 Postman 中测试添加新文章,首先选择 POST
作为方法,传入 addpost 端点,添加一个与上面类似的 Content-Type
标头,并在 RAW 部分提供将要添加的文章详细信息。设置完成后,点击“发送”。它将把您的记录添加到数据库中。
接下来测试使用 Postman 更新现有文章,首先选择 POST
方法,传入 updatepost 端点,添加一个与上面类似的 Content-Type
标头,并在 RAW 部分提供需要更新的文章详细信息。设置完成后,点击“发送”。它将更新数据库中您现有的记录。
接下来测试使用 Postman 获取可用文章列表,首先选择 GET
方法,传入 getposts 端点,添加一个与上面类似的 Content-Type
标头。设置完成后,点击“发送”。它将为您提供数据库中可用文章记录的列表。
接下来测试使用 Postman 获取单个文章,首先选择 GET
方法,传入 getpost
以及文章 ID 端点,添加一个与上面类似的 Content-Type
标头。设置完成后,点击“发送”。它将为您提供数据库中单个文章的详细信息。
接下来测试使用 Postman 删除现有文章,首先选择 POST
方法,传入 deletepost
端点以及需要删除的文章 ID,添加一个与上面类似的 Content-Type
标头。设置完成后,点击“发送”。它将从数据库中删除您现有的记录。
启用 CORS
哇……我们已经创建了一个 ASP.NET Core Web API,并添加了 Entity Framework Core 和实现了 CRUD 操作。最后,我们还测试了 API 的每个端点,一切正常。但是一个问题仍然存在,那就是 CORS 问题。如果您将此 API 部署到某个地方并与其他应用程序一起使用,那么它将抛出一些 CORS 相关异常。因此,让我们也配置 CORS,使这个 API 完美无缺。打开 Startup.cs 和 ConfigureService()
方法,并添加以下代码行来创建 CORS 策略。
services.AddCors(option => option.AddPolicy("MyBlogPolicy", builder => {
builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
}));
然后,在 Configure
方法中按如下方式使用此 CORS 策略:
app.UseCors("MyBlogPolicy");
以下是包含 CORS 实现的 Startup.cs 类的完整代码。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CoreServices.Models;
using CoreServices.Repository;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace CoreServices
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(option => option.AddPolicy("MyBlogPolicy", builder => {
builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
}));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddDbContext<BlogDBContext>(item => item.UseSqlServer
(Configuration.GetConnectionString("BlogDBConnection")));
services.AddScoped<IPostRepository, PostRepository>();
}
// This method gets called by the runtime.
// Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseCors("MyBlogPolicy");
app.UseMvc();
}
}
}
结论
所以,今天我们学习了如何使用 Entity Framework Core 在 ASP.NET Core Web API 中执行 CRUD 操作。
希望这篇文章对您有所帮助。请通过评论提供您的反馈,这将有助于我改进下一篇文章。如果您有任何疑问,请在下面的评论区提出您的问题,如果您喜欢这篇文章,请与您的朋友分享。谢谢!