基于.NET Core 3.x的电商系统实战项目ShopOnline
NET Core 3.x 是微软推出的跨平台、高性能、模块化的开发框架,适用于构建现代云原生和企业级应用。其核心特性包括:跨平台运行(支持Windows、Linux、macOS)、模块化设计(通过NuGet包按需加载)、高性能运行时(CoreCLR)以及对C#语言新特性的全面支持。在电商系统开发中,.NET Core 3.x 凭借其高并发处理能力和良好的扩展性,成为后端服务的理想选择。本章将从零开
简介:ShopOnline.DotNetCore3是一个基于.NET Core 3.x构建的完整电子商务平台示例,展示了使用ASP.NET Core MVC、Razor Pages、依赖注入、中间件和EF Core开发高性能跨平台Web应用的全过程。项目包含用户注册、商品浏览、购物车与订单处理等核心电商功能,适合学习Web开发最佳实践与现代Web技术栈的整合应用。
1. .NET Core 3.x框架介绍与搭建
.NET Core 3.x 是微软推出的跨平台、高性能、模块化的开发框架,适用于构建现代云原生和企业级应用。其核心特性包括:跨平台运行(支持Windows、Linux、macOS)、模块化设计(通过NuGet包按需加载)、高性能运行时(CoreCLR)以及对C#语言新特性的全面支持。在电商系统开发中,.NET Core 3.x 凭借其高并发处理能力和良好的扩展性,成为后端服务的理想选择。
本章将从零开始搭建一个基础的.NET Core 3.x开发环境。首先,访问 .NET Core 3.x SDK 下载页面 安装适用于你操作系统的SDK。
安装完成后,打开命令行工具,输入以下命令验证安装是否成功:
dotnet --version
预期输出类似如下内容(具体版本号可能略有不同):
3.1.426
接下来,我们使用 .NET CLI 创建一个最简单的 Web API 项目,作为后续章节的开发起点:
dotnet new webapi -n ShopApi
cd ShopApi
dotnet run
项目启动后,访问 https://localhost:5001/weatherforecast 可看到默认的天气预报API返回数据,表示环境搭建成功。
为了便于后续开发调试,推荐使用 Visual Studio 2019(或更高版本)或 Visual Studio Code 配合 C# 插件进行开发。通过以下命令可生成项目文件:
dotnet new sln
dotnet sln add ShopApi/ShopApi.csproj
最终生成的解决方案结构如下:
| 文件/目录 | 描述 |
|---|---|
| ShopApi.csproj | 项目配置文件 |
| Program.cs | 应用程序入口 |
| Startup.cs | 配置中间件与服务注册 |
| Controllers/ | 控制器存放目录 |
| appsettings.json | 配置文件 |
通过以上步骤,我们完成了 .NET Core 3.x 开发环境的搭建,并成功运行了一个基础 Web API 项目。下一章将深入讲解 ASP.NET Core MVC 架构的设计与实现原理。
2. ASP.NET Core MVC架构设计与实现
ASP.NET Core MVC 是基于模型-视图-控制器(Model-View-Controller)设计模式构建的 Web 开发框架,具有良好的分层结构和高度可测试性。它适用于构建响应式、模块化且易于维护的电商系统。本章将深入探讨 MVC 架构的组成、请求处理流程、路由配置以及在商城系统中的实际应用。
2.1 MVC架构的基本组成与工作流程
MVC 是一种广泛使用的架构模式,其核心在于将应用程序分为三个相互协作的部分: 模型(Model) 、 视图(View) 和 控制器(Controller) 。这种分离有助于管理复杂应用、提高代码的可维护性和可测试性。
2.1.1 模型(Model)与数据处理逻辑
模型负责封装与应用程序业务逻辑相关的数据,并提供操作数据的方法。在 ASP.NET Core MVC 中,模型通常由 POCO(Plain Old CLR Object)类实现,并通过 Entity Framework Core 与数据库进行交互。
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Description { get; set; }
}
逻辑分析:
Id:主键,用于唯一标识商品。Name:商品名称,用于展示。Price:商品价格,支持小数,适用于电商定价。Description:描述信息,用于商品详情页展示。
该模型类可以与 EF Core 配合使用,通过 DbContext 进行数据库的增删改查操作。
表格:模型类与数据库表的映射关系
| 模型属性名 | 数据类型 | 数据库字段名 | 说明 |
|---|---|---|---|
| Id | int | Id | 主键 |
| Name | string | Name | 商品名称 |
| Price | decimal | Price | 商品价格 |
| Description | string | Description | 商品描述信息 |
2.1.2 视图(View)与页面渲染机制
视图用于定义用户界面的呈现方式。在 ASP.NET Core MVC 中,视图使用 Razor 引擎进行渲染,支持 C# 代码与 HTML 的混合编写。
@model Product
<h2>@Model.Name</h2>
<p>价格:@Model.Price 元</p>
<p>描述:@Model.Description</p>
逻辑分析:
@model Product:指定当前视图绑定的模型类型。@Model.Name:访问模型的 Name 属性并输出。@Model.Price:访问价格属性,展示格式化价格信息。@Model.Description:展示商品详细描述。
Razor 引擎会在运行时将这些代码编译为 HTML,发送给客户端浏览器进行渲染。
流程图:视图渲染流程
graph TD
A[控制器处理请求] --> B[获取模型数据]
B --> C[传递模型至视图]
C --> D[调用Razor引擎渲染视图]
D --> E[生成HTML响应]
E --> F[返回给客户端浏览器]
2.1.3 控制器(Controller)的请求处理
控制器是 MVC 架构中的协调者,负责接收请求、调用模型方法、返回视图或 JSON 数据。
public class ProductController : Controller
{
private readonly IProductService _productService;
public ProductController(IProductService productService)
{
_productService = productService;
}
public IActionResult Details(int id)
{
var product = _productService.GetProductById(id);
if (product == null)
{
return NotFound();
}
return View(product);
}
}
逻辑分析:
IProductService:通过依赖注入获取服务,实现松耦合。Details(int id):接收商品 ID,调用服务获取数据。View(product):将模型传递给视图进行渲染。
控制器的职责是处理 HTTP 请求并决定响应内容,它不应包含复杂的业务逻辑,而应调用服务层进行处理。
2.2 路由配置与请求生命周期
在 ASP.NET Core MVC 中,路由决定了如何将请求 URL 映射到控制器和操作方法。理解路由机制和请求处理生命周期是构建高效电商应用的关键。
2.2.1 路由规则定义与匹配机制
默认的路由规则如下:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
参数说明:
controller=Home:默认控制器为 Home。action=Index:默认动作为 Index。id?:可选参数,用于传递资源 ID。
示例匹配:
/Product/Details/5→ ProductController.Details(5)/User/Login→ UserController.Login()
2.2.2 请求管道中的中间组件交互
ASP.NET Core 使用中间件(Middleware)来构建请求处理管道。每个中间件组件按顺序执行,处理请求或传递给下一个组件。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
流程图:请求管道流程
graph TD
A[客户端请求] --> B[UseDeveloperExceptionPage]
B --> C[UseRouting]
C --> D[UseAuthentication]
D --> E[UseAuthorization]
E --> F[UseEndpoints]
F --> G[控制器处理]
中间件作用说明:
UseDeveloperExceptionPage():开发环境显示详细错误信息。UseRouting():路由解析。UseAuthentication():身份验证。UseAuthorization():权限验证。UseEndpoints():执行控制器方法。
2.2.3 异常处理与日志记录集成
ASP.NET Core 提供了多种方式来处理异常和记录日志。
自定义异常处理中间件:
public class ExceptionMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ExceptionMiddleware> _logger;
public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext httpContext)
{
try
{
await _next(httpContext);
}
catch (Exception ex)
{
_logger.LogError(ex, "An unhandled exception occurred.");
httpContext.Response.Redirect("/Error/InternalServerError");
}
}
}
注册中间件:
app.UseMiddleware<ExceptionMiddleware>();
日志配置(在 appsettings.json 中):
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
2.3 商城系统中的MVC模块划分实践
在实际的电商系统中,合理的模块划分有助于提升代码的可维护性和团队协作效率。本节将以商品管理、订单处理和用户界面为例,展示 MVC 的模块划分与实现。
2.3.1 商品管理模块的控制器设计
商品管理模块主要负责商品信息的展示、新增、编辑与删除。
[Area("Admin")]
[Authorize(Roles = "Admin")]
public class ProductController : Controller
{
private readonly IProductService _productService;
public ProductController(IProductService productService)
{
_productService = productService;
}
public IActionResult Index()
{
var products = _productService.GetAllProducts();
return View(products);
}
[HttpGet]
public IActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(Product product)
{
if (ModelState.IsValid)
{
_productService.AddProduct(product);
return RedirectToAction("Index");
}
return View(product);
}
}
逻辑分析:
[Area("Admin")]:使用 Area 模块化管理后台功能。[Authorize(Roles = "Admin")]:仅管理员可访问。Index():获取所有商品并展示。Create():展示新增页面。Create(Product product):处理表单提交,保存商品信息。
2.3.2 订单处理模块的模型构建
订单模块通常涉及订单头、订单明细、支付状态等信息。
public class Order
{
public int OrderId { get; set; }
public DateTime OrderDate { get; set; }
public string UserId { get; set; }
public decimal TotalAmount { get; set; }
public string Status { get; set; }
public List<OrderDetail> OrderDetails { get; set; }
}
public class OrderDetail
{
public int OrderDetailId { get; set; }
public int ProductId { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
}
表格:订单模型字段说明
| 字段名 | 类型 | 说明 |
|---|---|---|
| OrderId | int | 订单唯一标识 |
| OrderDate | DateTime | 下单时间 |
| UserId | string | 用户ID |
| TotalAmount | decimal | 订单总金额 |
| Status | string | 订单状态(如已发货) |
| OrderDetails | List | 关联的订单明细列表 |
2.3.3 用户界面模块的视图组织方式
在电商系统中,用户界面模块包括商品列表页、商品详情页、购物车页等。合理组织视图结构有助于维护和扩展。
视图结构示例:
/Views
/Product
Index.cshtml
Details.cshtml
/Cart
Index.cshtml
Checkout.cshtml
/Shared
_Layout.cshtml
_Menu.cshtml
特点说明:
_Layout.cshtml:共享布局页面,定义网站整体结构。_Menu.cshtml:部分视图,用于导航菜单。Index.cshtml:商品列表页,展示多个商品。Details.cshtml:商品详情页,展示单个商品信息。
示例:_Layout.cshtml 共享布局
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
<div class="container">
@Html.Partial("_Menu")
@RenderBody()
</div>
</body>
</html>
逻辑分析:
<title>@ViewBag.Title</title>:动态设置页面标题。@Html.Partial("_Menu"):加载部分视图菜单。@RenderBody():占位符,用于插入子视图内容。
通过本章的深入分析与代码实践,我们全面了解了 ASP.NET Core MVC 的架构组成、请求处理流程、路由配置以及在电商系统中的模块划分与实现方式。这些内容为构建模块化、高可维护性的商城系统奠定了坚实基础。
3. Razor Pages页面开发实践
Razor Pages 是 ASP.NET Core 中一种轻量级的页面驱动开发模型,适用于构建以页面为中心的应用程序。与传统的 MVC 架构相比,Razor Pages 更适合中小型项目或页面逻辑相对独立的场景,尤其在电商系统中,常用于商品展示、用户注册、登录等界面的开发。
本章将从 Razor Pages 的基本结构与语法入手,结合电商项目中的实际场景,深入讲解其在商品详情页、用户交互页面中的应用,并进一步探讨如何通过布局共享、组件封装等技术提升开发效率与代码复用性。
3.1 Razor Pages的基本结构与语法
Razor Pages 是 ASP.NET Core 提供的一种基于页面的编程模型,它将页面逻辑和 UI 紧密结合,简化了页面处理流程。本节将详细介绍 Razor Pages 的基本结构、页面模型绑定机制以及常用的 Razor 语法。
3.1.1 页面模型与页面视图的绑定
Razor Pages 的核心在于“页面即模型”的设计思想。每个 .cshtml 页面都对应一个 .cshtml.cs 文件,后者用于定义页面模型(PageModel),处理页面逻辑。
目录结构示例:
/Pages
/Product
ProductDetails.cshtml
ProductDetails.cshtml.cs
ProductDetails.cshtml.cs(页面模型)代码如下:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace ECommerceApp.Pages.Product
{
public class ProductDetailsModel : PageModel
{
public string ProductName { get; set; }
public decimal Price { get; set; }
public void OnGet(int id)
{
// 模拟从数据库获取数据
ProductName = "iPhone 13";
Price = 6999.00m;
}
}
}
ProductDetails.cshtml(页面视图)代码如下:
@page "{id}"
@model ECommerceApp.Pages.Product.ProductDetailsModel
<h1>@Model.ProductName</h1>
<p>价格:@Model.Price.ToString("C")</p>
逐行代码解析:
@page "{id}":表示该页面接受一个名为id的路由参数。@model:指定当前页面使用的模型类。OnGet(int id):页面模型中的处理方法,在 GET 请求时被调用,用于初始化页面数据。
页面绑定机制说明:
- Razor Pages 中的页面模型继承自
PageModel,通过OnGet或OnPost方法处理请求。 - 视图文件通过
@model引用对应的模型类,从而实现数据绑定。
3.1.2 常用Razor语法与HTML混合编程
Razor 语法允许将 C# 代码嵌入 HTML 中,实现动态页面渲染。以下是一些常用的 Razor 语法示例。
条件渲染:
@if (Model.Price > 5000)
{
<p>这是一款高端商品。</p>
}
else
{
<p>性价比不错。</p>
}
循环渲染:
<ul>
@foreach (var feature in Model.Features)
{
<li>@feature</li>
}
</ul>
参数说明:
@if、@foreach:用于控制 HTML 输出逻辑。Model.Features:页面模型中定义的字符串列表,用于展示商品特性。
混合编程示例表格:
| Razor语法 | 用途 | 示例 |
|---|---|---|
@model |
引用模型类 | @model MyPageModel |
@page |
定义页面路由 | @page "{id}" |
@if / @else |
条件判断 | @if (true) { <p>显示内容</p> } |
@foreach |
遍历集合 | @foreach (var item in list) { <li>@item</li> } |
@functions |
定义辅助方法 | @functions { string FormatPrice(decimal p) { return p.ToString("C"); } } |
代码说明:
- Razor 语法可以与 HTML 紧密结合,提升页面渲染灵活性。
- 可通过
@functions定义辅助方法,提高代码复用性。
3.2 Razor Pages在电商项目中的应用场景
Razor Pages 在电商系统中常用于构建静态页面、商品详情页、用户注册与登录等场景。相比 MVC,它更适合页面逻辑独立、前后端交互较少的场景。
3.2.1 商品详情页的动态生成
商品详情页是电商系统中访问频率最高的页面之一。使用 Razor Pages 可以实现快速响应和动态数据绑定。
页面模型代码示例:
public class ProductDetailsModel : PageModel
{
public Product Product { get; set; }
private readonly IProductService _productService;
public ProductDetailsModel(IProductService productService)
{
_productService = productService;
}
public void OnGet(int id)
{
Product = _productService.GetProductById(id);
}
}
视图页面代码示例:
@model ECommerceApp.Pages.Product.ProductDetailsModel
<h2>@Model.Product.Name</h2>
<p>价格:@Model.Product.Price.ToString("C")</p>
<p>库存:@Model.Product.Stock</p>
执行逻辑说明:
- 页面模型中注入了
IProductService,用于从数据库获取商品信息。 - 页面通过
OnGet方法根据id查询商品详情,并绑定到视图中。
mermaid 流程图展示页面加载流程:
graph TD
A[用户请求商品详情页] --> B[框架解析路由参数id]
B --> C[调用OnGet方法]
C --> D[调用ProductService获取商品数据]
D --> E[绑定数据到页面模型]
E --> F[渲染视图并返回HTML]
3.2.2 用户注册与登录页面的交互实现
用户注册和登录页面是电商系统中常见的表单交互场景。Razor Pages 支持 OnPost 方法处理表单提交。
页面模型代码:
public class RegisterModel : PageModel
{
[BindProperty]
public User User { get; set; }
public string Message { get; set; }
public void OnPost()
{
if (ModelState.IsValid)
{
// 模拟注册逻辑
Message = $"注册成功:{User.Email}";
}
else
{
Message = "请输入有效的邮箱和密码";
}
}
}
视图页面代码:
@model RegisterModel
<form method="post">
<input asp-for="User.Email" placeholder="邮箱" />
<input asp-for="User.Password" type="password" placeholder="密码" />
<button type="submit">注册</button>
</form>
<p>@Model.Message</p>
参数说明:
[BindProperty]:用于绑定表单字段,自动将 POST 数据填充到模型属性。asp-for:Razor 标签帮助器,用于生成带有验证信息的输入框。
执行逻辑说明:
- 用户提交表单后,框架自动调用
OnPost方法。 ModelState.IsValid判断表单输入是否合法。- 成功后设置提示信息并返回视图。
3.3 页面布局与组件复用技巧
在电商项目中,页面布局的一致性和组件复用性对提升开发效率和用户体验至关重要。Razor Pages 提供了多种方式实现布局共享和组件封装。
3.3.1 共享布局页面的设计
通过 _Layout.cshtml 实现页面布局统一。
_Layout.cshtml 示例代码:
<!DOCTYPE html>
<html>
<head>
<title>商城系统</title>
</head>
<body>
<header>
<h1>欢迎来到我们的商城</h1>
</header>
<div>
@RenderBody()
</div>
<footer>
<p>© 2025 商城版权所有</p>
</footer>
</body>
</html>
页面中引用布局:
@{
Layout = "_Layout";
}
参数说明:
@RenderBody():表示页面内容插入的位置。Layout属性用于指定当前页面使用的布局文件。
3.3.2 部分视图与标签帮助器的应用
部分视图(Partial View)用于复用 UI 组件,例如商品推荐、侧边栏等。
_ProductSummary.cshtml 示例:
@model Product
<div class="product">
<h3>@Model.Name</h3>
<p>价格:@Model.Price.ToString("C")</p>
</div>
在主页面中调用:
@await Html.PartialAsync("_ProductSummary", Model.Product)
标签帮助器(Tag Helpers)示例:
<a asp-page="/Product/Details" asp-route-id="@Model.Product.Id">查看详情</a>
参数说明:
PartialAsync:异步加载部分视图。asp-page:指定跳转页面路径。asp-route-id:传递路由参数。
3.3.3 页面组件的封装与调用
页面组件(Page Component)是一种可复用的 UI 组件,适用于需要动态加载的区域,如购物车摘要、推荐商品等。
组件模型代码:
public class ShoppingCartSummaryComponent : PageComponent
{
private readonly IShoppingCartService _cartService;
public ShoppingCartSummaryComponent(IShoppingCartService cartService)
{
_cartService = cartService;
}
public IViewComponentResult Invoke()
{
var cartSummary = _cartService.GetSummary();
return View(cartSummary);
}
}
组件视图(Default.cshtml):
@model ShoppingCartSummary
<div class="cart-summary">
<p>共 @Model.ItemCount 件商品,总计 @Model.TotalPrice.ToString("C")</p>
</div>
在页面中调用组件:
@await Component.InvokeAsync("ShoppingCartSummary")
执行逻辑说明:
- 页面组件通过
Invoke方法生成视图数据。 - 使用
Component.InvokeAsync在任意页面中动态加载组件。
本章系统讲解了 Razor Pages 的基本结构、在电商项目中的实际应用以及页面布局与组件复用技巧。通过本章内容,开发者可以快速构建结构清晰、逻辑独立的页面,并实现高效的代码复用与页面管理。
4. 依赖注入(DI)系统配置与使用
依赖注入(Dependency Injection,简称 DI)是现代软件架构中实现解耦、提升可测试性和可维护性的重要机制。在 .NET Core 中,DI 系统是框架的核心组成部分之一,它支持构造函数注入、属性注入、方法注入等多种方式,并提供服务生命周期的精细控制。本章将深入探讨 DI 的原理、在 .NET Core 中的实现方式,以及其在电商系统中的典型应用。
4.1 依赖注入原理与核心概念
4.1.1 控制反转(IoC)与服务注册
控制反转(Inversion of Control,IoC) 是一种设计原则,它将对象的创建和管理交给框架或容器来完成,而不是由对象自身直接创建其依赖项。这种设计可以显著降低组件之间的耦合度,使系统更易于扩展和维护。
在 .NET Core 中,DI 容器负责管理对象的生命周期及其依赖关系。服务的注册是通过 Startup.cs 文件中的 ConfigureServices 方法完成的。
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IProductService, ProductService>();
services.AddScoped<ICartService, CartService>();
services.AddTransient<IEmailService, EmailService>();
}
-
AddSingleton:整个应用程序生命周期内共享同一个实例。 -
AddScoped:每个请求生命周期内共享一个实例。 -
AddTransient:每次请求都创建一个新实例。
代码逻辑分析 :
- 第一行注册了一个单例服务IProductService,适用于不需要状态的服务,如缓存服务。
- 第二行注册了作用域服务ICartService,适用于每个用户请求中需要独立状态的场景,如购物车。
- 第三行注册了瞬态服务IEmailService,适用于轻量级、无状态的对象,如邮件发送服务。
参数说明 :
- IServiceCollection 是用于注册服务的集合。
- 泛型接口 IProductService 和实现类 ProductService 必须匹配。
4.1.2 生命周期管理(Singleton、Scoped、Transient)
服务的生命周期决定了对象在应用程序中的创建与销毁方式。选择正确的生命周期对性能和资源管理至关重要。
| 生命周期类型 | 描述 | 适用场景 |
|---|---|---|
| Singleton | 一个实例贯穿整个应用生命周期 | 日志服务、缓存服务 |
| Scoped | 每个请求创建一个实例 | 数据库上下文、用户会话 |
| Transient | 每次调用都新建实例 | 无状态工具类、轻量级服务 |
流程图:服务生命周期图解
graph TD
A[请求开始] --> B[创建Scoped实例]
A --> C[获取Singleton实例]
B --> D[调用Transient服务]
C --> E[调用Scoped服务]
D --> F[返回Transient实例]
E --> G[返回Scoped实例]
A --> H[请求结束]
H --> I[释放Scoped实例]
4.2 在.NET Core中实现依赖注入
4.2.1 服务接口与实现类的定义
定义服务接口和实现类是使用 DI 的第一步。以商品服务为例:
public interface IProductService
{
Product GetProductById(int id);
}
public class ProductService : IProductService
{
public Product GetProductById(int id)
{
// 模拟数据库查询
return new Product { Id = id, Name = "Sample Product", Price = 99.99m };
}
}
代码逻辑分析 :
- 第一个接口IProductService定义了获取商品的方法。
- 第二个类ProductService实现了该接口,内部模拟了数据库查询。
参数说明 :
- Product 是一个简单的实体类,包含商品的基本属性。
4.2.2 构造函数注入与方法注入方式
构造函数注入 是最常见、推荐的方式,它确保对象在创建时就具有所有必需的依赖。
public class ProductController : Controller
{
private readonly IProductService _productService;
public ProductController(IProductService productService)
{
_productService = productService;
}
public IActionResult Details(int id)
{
var product = _productService.GetProductById(id);
return View(product);
}
}
代码逻辑分析 :
-ProductController通过构造函数接收IProductService的实例。
- 在Details方法中调用服务获取商品信息并返回视图。
方法注入 则是在特定方法中传入依赖项,适用于某些特殊情况,如:
public IActionResult Search([FromServices] IProductService productService, string keyword)
{
var results = productService.Search(keyword);
return View(results);
}
说明 :
-[FromServices]特性指示框架从 DI 容器中解析服务。
4.2.3 服务定位器模式与最佳实践
服务定位器模式 是一种反模式,它通过 IServiceProvider 直接获取服务实例,而非通过构造函数注入。
public class OrderService
{
private readonly IServiceProvider _serviceProvider;
public OrderService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void ProcessOrder()
{
var emailService = (IEmailService)_serviceProvider.GetService(typeof(IEmailService));
emailService.Send("order@example.com", "Your order is processed.");
}
}
代码逻辑分析 :
- 使用IServiceProvider.GetService()获取服务实例。
- 虽然灵活,但违背了依赖注入的设计原则,不推荐在项目中广泛使用。
最佳实践建议 :
- 始终使用构造函数注入。
- 避免服务定位器模式。
- 将服务生命周期设置为最合适的级别,避免内存泄漏。
4.3 DI在电商系统中的典型应用
4.3.1 商品仓储服务的注入与调用
在电商系统中,商品仓储服务通常负责与数据库交互。使用 DI 可以方便地将仓储服务注入到控制器或业务逻辑中。
public interface IProductRepository
{
IEnumerable<Product> GetAll();
Product GetById(int id);
void Add(Product product);
}
public class ProductRepository : IProductRepository
{
private readonly AppDbContext _context;
public ProductRepository(AppDbContext context)
{
_context = context;
}
public IEnumerable<Product> GetAll()
{
return _context.Products.ToList();
}
public Product GetById(int id)
{
return _context.Products.Find(id);
}
public void Add(Product product)
{
_context.Products.Add(product);
_context.SaveChanges();
}
}
代码逻辑分析 :
-IProductRepository定义了商品仓储的基本操作。
-ProductRepository接收AppDbContext(EF Core 数据上下文)并通过其操作数据库。
参数说明 :
- AppDbContext 是 EF Core 中的数据上下文类,通过 DI 注入。
4.3.2 日志服务在多个组件中的复用
日志服务通常以 Singleton 生命周期注册,确保整个应用程序中统一使用同一个日志记录器。
public interface ILoggerService
{
void Log(string message);
}
public class LoggerService : ILoggerService
{
public void Log(string message)
{
// 写入日志文件或控制台
Console.WriteLine($"[LOG] {DateTime.Now}: {message}");
}
}
在控制器中使用:
public class OrderController : Controller
{
private readonly ILoggerService _logger;
public OrderController(ILoggerService logger)
{
_logger = logger;
}
public IActionResult PlaceOrder(Order order)
{
_logger.Log("Order placed.");
return Ok();
}
}
说明 :
-LoggerService被注册为 Singleton,确保日志记录器在多个组件中共享。
- 每个组件通过构造函数注入即可获得日志服务。
4.3.3 缓存服务的统一管理与配置
缓存服务常用于提升电商系统的响应速度。在 .NET Core 中,可以使用内置的 IMemoryCache 或自定义缓存服务。
public interface ICacheService
{
T Get<T>(string key);
void Set<T>(string key, T value, TimeSpan expiration);
}
public class MemoryCacheService : ICacheService
{
private readonly IMemoryCache _cache;
public MemoryCacheService(IMemoryCache cache)
{
_cache = cache;
}
public T Get<T>(string key)
{
return _cache.Get<T>(key);
}
public void Set<T>(string key, T value, TimeSpan expiration)
{
_cache.Set(key, value, expiration);
}
}
代码逻辑分析 :
-IMemoryCache是 .NET Core 提供的内存缓存服务。
-MemoryCacheService封装了缓存操作,提供统一接口。
注册服务 :
services.AddSingleton<ICacheService, MemoryCacheService>();
典型应用场景 :
- 缓存热门商品信息。
- 缓存用户会话数据。
- 缓存频繁查询的结果,减少数据库压力。总结性说明 :
通过合理使用依赖注入机制,电商系统中的各个组件可以高效协作,降低耦合,提升系统的可测试性和可维护性。同时,生命周期的合理配置和设计模式的正确应用,也是构建高质量 .NET Core 应用的关键所在。
5. 中间件(Middleware)开发与管道构建
中间件是 ASP.NET Core 应用程序处理请求和响应的核心组件,它们按照定义的顺序依次构成一个“请求管道”,每个中间件都有机会处理请求或响应,甚至决定是否将请求传递给下一个中间件。本章将深入探讨中间件的开发机制、执行顺序、注册方式,并结合电商系统的实际应用场景,展示如何通过中间件实现日志记录、权限验证、请求处理等功能。
5.1 中间件的基本概念与执行机制
在 ASP.NET Core 中,中间件(Middleware)是通过 Use 、 Run 、 Map 等方法注册到请求管道中的。中间件的执行顺序直接影响应用程序的行为,因此理解其执行流程至关重要。
5.1.1 中间件的作用与分类
中间件主要分为以下几类:
| 类型 | 说明 | 示例 |
|---|---|---|
| 终止中间件 | 不调用下一个中间件,直接返回响应 | Run() |
| 管道中间件 | 调用下一个中间件,并可能在前后添加逻辑 | Use() |
| 分支中间件 | 根据请求路径或其他条件,选择性地执行中间件 | Map() |
5.1.2 请求管道的构建流程
ASP.NET Core 的请求管道由 Startup.cs 文件中的 Configure 方法构建。每个中间件通过 app.Use() 方法添加到管道中,其执行顺序决定了请求的处理逻辑。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<RequestLoggingMiddleware>();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
代码解析:
UseDeveloperExceptionPage():在开发环境下启用异常页面,便于调试。UseRouting():启用路由匹配功能。UseAuthentication()和UseAuthorization():启用身份验证和授权机制。UseMiddleware<RequestLoggingMiddleware>():注册自定义的请求日志记录中间件。UseEndpoints():定义端点路由。
5.1.3 中间件执行顺序的重要性
中间件的注册顺序直接影响其执行顺序,例如:
graph TD
A[客户端请求] --> B[UseDeveloperExceptionPage]
B --> C[UseRouting]
C --> D[UseAuthentication]
D --> E[UseAuthorization]
E --> F[UseMiddleware<RequestLoggingMiddleware>]
F --> G[UseEndpoints]
G --> H[响应返回]
如上图所示,每个中间件都按注册顺序依次处理请求。若中间件未调用 next() ,请求将在此处终止。
5.2 自定义中间件的开发与注册
ASP.NET Core 允许开发者编写自定义中间件,以满足特定业务逻辑的需求,例如日志记录、权限检查、请求拦截等。
5.2.1 创建自定义中间件类
自定义中间件通常是一个类,其构造函数接收一个 RequestDelegate 参数,并提供一个 Invoke 或 InvokeAsync 方法来处理请求。
public class RequestLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<RequestLoggingMiddleware> _logger;
public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
// 请求前处理
var requestPath = context.Request.Path;
_logger.LogInformation($"开始处理请求:{requestPath}");
// 传递给下一个中间件
await _next(context);
// 请求后处理
var responseStatusCode = context.Response.StatusCode;
_logger.LogInformation($"请求完成,状态码:{responseStatusCode}");
}
}
参数说明:
RequestDelegate:表示请求管道中的下一个中间件。ILogger<T>:用于日志记录,由依赖注入系统自动注入。HttpContext:包含当前请求和响应的上下文信息。
5.2.2 注册自定义中间件
在 Startup.cs 的 Configure 方法中注册中间件:
app.UseMiddleware<RequestLoggingMiddleware>();
也可以通过扩展方法封装注册逻辑:
public static class RequestLoggingMiddlewareExtensions
{
public static IApplicationBuilder UseRequestLogging(this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestLoggingMiddleware>();
}
}
然后在 Configure 中使用:
app.UseRequestLogging();
5.3 中间件在电商系统中的典型应用场景
在电商系统中,中间件可以广泛应用于多个业务模块,以下是一些典型场景:
5.3.1 请求日志记录与性能监控
在电商系统中,记录每个请求的访问路径、耗时、用户信息等,有助于分析系统行为和优化性能。
public class PerformanceMonitoringMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
public PerformanceMonitoringMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
{
_next = next;
_logger = loggerFactory.CreateLogger<PerformanceMonitoringMiddleware>();
}
public async Task InvokeAsync(HttpContext context)
{
var stopwatch = new Stopwatch();
stopwatch.Start();
await _next(context);
stopwatch.Stop();
_logger.LogInformation($"请求 {context.Request.Path} 耗时 {stopwatch.ElapsedMilliseconds} ms");
}
}
该中间件通过记录请求耗时,帮助开发人员发现性能瓶颈。
5.3.2 权限验证与身份拦截
在用户访问敏感接口(如订单管理、用户资料修改)时,可以通过中间件进行权限拦截。
public class AdminAccessMiddleware
{
private readonly RequestDelegate _next;
public AdminAccessMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var user = context.User;
if (!user.Identity.IsAuthenticated || !user.IsInRole("Admin"))
{
context.Response.StatusCode = StatusCodes.Status403Forbidden;
await context.Response.WriteAsync("无权访问");
return;
}
await _next(context);
}
}
说明:
- 通过
context.User获取当前用户信息。 - 检查用户是否为管理员,若非管理员则返回 403 状态码并终止请求。
5.3.3 请求路径重定向与多语言支持
电商系统通常支持多语言,可以通过中间件根据用户浏览器设置自动重定向到对应语言版本。
public class LanguageRedirectMiddleware
{
private readonly RequestDelegate _next;
public LanguageRedirectMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var path = context.Request.Path.Value.ToLower();
var acceptLanguage = context.Request.Headers["Accept-Language"].ToString();
if (string.IsNullOrEmpty(acceptLanguage))
{
acceptLanguage = "en-US";
}
var defaultLanguage = acceptLanguage.Split(',').First().Split(';').First();
if (!path.StartsWith("/" + defaultLanguage))
{
var newPath = $"/{defaultLanguage}{path}";
context.Response.Redirect(newPath);
return;
}
await _next(context);
}
}
逻辑分析:
- 获取用户请求头中的
Accept-Language。 - 提取首选语言。
- 若路径中不包含该语言前缀,则重定向到对应语言版本。
5.4 中间件的性能优化与最佳实践
中间件作为请求处理链中的核心组件,其性能和结构设计直接影响整个应用程序的效率和可维护性。
5.4.1 中间件的执行顺序优化
中间件应按照逻辑顺序排列,避免不必要的处理:
- 认证、授权类中间件应尽早执行。
- 日志记录、性能监控类中间件应在整个管道前后注册。
- 静态资源处理中间件(如
UseStaticFiles)应放在认证之前,以提升性能。
5.4.2 避免中间件中的阻塞操作
中间件应尽量使用异步方法( InvokeAsync ),避免同步阻塞操作影响并发性能。
5.4.3 使用中间件分组与条件执行
可以通过 Map 和 MapWhen 实现中间件的条件执行:
app.Map("/api", builder =>
{
builder.UseMiddleware<ApiAuthenticationMiddleware>();
builder.UseMiddleware<ApiRateLimitingMiddleware>();
});
5.4.4 使用中间件工厂实现依赖注入
若中间件需要复杂的依赖注入逻辑,可以使用 IMiddlewareFactory 实现更灵活的注册方式。
public class CustomMiddlewareFactory : IMiddlewareFactory
{
private readonly IServiceProvider _serviceProvider;
public CustomMiddlewareFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IMiddleware CreateInstance<TMiddleware>()
{
return _serviceProvider.GetRequiredService<TMiddleware>() as IMiddleware;
}
public void Release(IMiddleware middleware)
{
// 可选释放逻辑
}
}
5.5 小结
本章深入讲解了中间件的开发机制、执行流程以及在电商系统中的典型应用场景。通过自定义中间件,我们可以实现请求日志记录、权限控制、多语言重定向等功能,同时结合依赖注入和条件执行机制,使中间件更具灵活性和可维护性。合理安排中间件的执行顺序、优化性能、使用异步处理,是构建高效、安全、可扩展的电商系统的重要保障。
6. Kestrel Web服务器配置与部署
Kestrel 是 .NET Core 默认的跨平台 Web 服务器,专为高性能、低延迟的网络请求处理而设计。它在电商系统中扮演着至关重要的角色:作为前端网关,承载着商品浏览、订单提交、用户登录等关键业务。本章将详细介绍 Kestrel 的配置方法、SSL 安全设置、反向代理集成,以及在 Windows、Linux、macOS 上的部署策略,帮助开发者构建稳定、安全、高效的电商平台。
6.1 Kestrel 的基本配置与核心特性
6.1.1 什么是 Kestrel?
Kestrel 是一个基于 libuv 的跨平台 Web 服务器,最初是 ASP.NET 5(现在为 ASP.NET Core)的一部分。与传统的 IIS 或 Apache 不同,Kestrel 是一个轻量级的服务器,专注于处理 HTTP 请求并提供高性能的 I/O 操作。
其核心特性包括:
| 特性 | 描述 |
|---|---|
| 跨平台 | 支持 Windows、Linux、macOS |
| 高性能 | 基于 libuv 实现异步 I/O,支持高并发 |
| 灵活配置 | 可通过 appsettings.json 、代码配置绑定端口和SSL |
| 多协议支持 | 支持 HTTP/1.1、HTTP/2、HTTPS、WebSockets |
6.1.2 默认配置方式
Kestrel 的默认配置通常在 Program.cs 中通过 CreateDefaultBuilder 方法自动配置。其核心配置逻辑如下:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseKestrel();
webBuilder.UseStartup<Startup>();
});
}
UseKestrel():启用 Kestrel 作为 Web 服务器。UseStartup<Startup>():指定启动类,用于配置中间件、服务等。
6.1.3 自定义 Kestrel 配置
你可以通过 appsettings.json 或代码方式自定义 Kestrel 的行为。例如:
示例: appsettings.json 中配置 Kestrel
{
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://localhost:5000"
},
"Https": {
"Url": "https://localhost:5001",
"Certificate": {
"Path": "cert.pfx",
"Password": "your_password"
}
}
}
}
}
示例:代码方式配置 Kestrel
webBuilder.ConfigureKestrel(serverOptions =>
{
serverOptions.ListenAnyIP(5000); // HTTP
serverOptions.ListenAnyIP(5001, listenOptions =>
{
listenOptions.UseHttps("cert.pfx", "your_password");
});
});
ListenAnyIP():监听所有 IP 地址。UseHttps():启用 HTTPS 并指定证书路径和密码。
6.2 SSL 配置与安全通信
在电商系统中,保障用户敏感信息(如信用卡、登录凭证)的安全至关重要。Kestrel 支持 HTTPS,可通过配置 SSL 证书实现安全通信。
6.2.1 获取与安装 SSL 证书
SSL 证书可以从权威证书颁发机构(CA)获取,如 Let’s Encrypt、DigiCert、GoDaddy 等。你也可以使用开发环境中的自签名证书进行测试。
示例:使用 .NET CLI 创建自签名证书
dotnet dev-certs https -ep cert.pfx -p your_password
-ep:导出证书路径。-p:证书密码。
6.2.2 在 Kestrel 中启用 HTTPS
启用 HTTPS 的核心代码如下:
serverOptions.Listen(IPAddress.Any, 5001, options =>
{
options.UseHttps("cert.pfx", "your_password");
});
UseHttps():启用 HTTPS 并加载证书。
6.2.3 强制 HTTPS 重定向
为了确保所有请求都经过加密通道,可以启用 HTTPS 重定向:
services.AddHttpsRedirection(options =>
{
options.HttpsPort = 5001;
});
HttpsPort:重定向的目标 HTTPS 端口。
然后在中间件中启用:
app.UseHttpsRedirection();
6.3 反向代理集成与性能优化
虽然 Kestrel 性能优异,但在生产环境中通常不建议直接暴露给公网。推荐使用反向代理服务器(如 Nginx、IIS、HAProxy)来处理静态资源、负载均衡、SSL 终止等任务,将请求转发给 Kestrel。
6.3.1 使用 Nginx 作为反向代理
示例:Nginx 配置文件 /etc/nginx/sites-available/default
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
proxy_pass:将请求代理到本地运行的 Kestrel。proxy_set_header:设置请求头,确保 Kestrel 正确识别原始主机名和协议。
启用 HTTPS 的 Nginx 配置
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
location / {
proxy_pass http://localhost:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
X-Forwarded-Proto:告知 Kestrel 请求是 HTTPS,防止重定向死循环。
6.3.2 Kestrel 与反向代理的协作机制
graph TD
A[Client] --> B[Nginx]
B --> C[Kestrel]
C --> B
B --> A
- Nginx 接收所有外部请求,处理 SSL、静态资源、限流等。
- Kestrel 只需处理动态请求,减轻负担。
6.3.3 前置中间件优化建议
在 Startup.cs 中启用以下中间件,以提升性能与兼容性:
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
XForwardedFor:获取客户端真实 IP。XForwardedProto:识别 HTTPS 请求。
6.4 不同操作系统的部署策略
6.4.1 Windows 部署
在 Windows 上,你可以将应用发布为自托管应用,或托管在 IIS 下作为反向代理。
发布命令
dotnet publish -c Release -o ./publish
启动命令
dotnet YourApp.dll
使用 IIS 作为反向代理
IIS 可以通过 Application Request Routing(ARR)模块代理请求到 Kestrel。
6.4.2 Linux 部署(以 Ubuntu 为例)
安装 .NET Runtime
sudo apt-get install -y dotnet-sdk-3.1
创建服务文件 /etc/systemd/system/yourapp.service
[Unit]
Description=My .NET Core E-commerce App
[Service]
WorkingDirectory=/var/www/yourapp
ExecStart=/usr/bin/dotnet /var/www/yourapp/YourApp.dll
Restart=always
RestartSec=10
SyslogIdentifier=dotnet-yourapp
User=www-data
Environment=ASPNETCORE_ENVIRONMENT=Production
[Install]
WantedBy=multi-user.target
ExecStart:指定启动命令。User:运行用户。Environment:设置环境变量。
启动服务
sudo systemctl enable yourapp
sudo systemctl start yourapp
6.4.3 macOS 部署
macOS 支持与 Linux 类似,可以使用 launchd 管理服务。
示例: ~/Library/LaunchAgents/com.yourapp.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.yourapp</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/dotnet</string>
<string>/path/to/YourApp.dll</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>WorkingDirectory</key>
<string>/path/to</string>
</dict>
</plist>
加载服务
launchctl load ~/Library/LaunchAgents/com.yourapp.plist
launchctl start com.yourapp
6.5 性能调优与监控建议
6.5.1 连接池与超时设置
在 appsettings.json 中配置 Kestrel 的连接池参数:
{
"Kestrel": {
"ConnectionLimits": {
"MaxConcurrentConnections": 10000,
"MaxConcurrentUpgradedConnections": 10000,
"MaxRequestBodySize": 10485760
}
}
}
MaxConcurrentConnections:最大并发连接数。MaxRequestBodySize:最大请求体大小(字节)。
6.5.2 日志与监控集成
推荐集成 Serilog、Application Insights 等日志工具。
示例:集成 Serilog
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.File("logs/myapp-.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
Host.CreateDefaultBuilder(args)
.UseSerilog()
...
6.5.3 使用 Application Insights 进行性能监控
services.AddApplicationInsightsTelemetry("YOUR_INSTRUMENTATION_KEY");
6.6 小结
Kestrel 作为 .NET Core 内建的高性能 Web 服务器,在电商系统中承担着核心的网络通信职责。通过合理配置 SSL、集成反向代理(如 Nginx)、以及在不同操作系统上的部署优化,可以显著提升系统的安全性和性能。结合日志、监控工具,开发者可以实时掌握系统运行状态,为高并发、大流量的电商平台保驾护航。
本章内容为后续章节(如数据库连接、API 网关、分布式部署)打下坚实基础,帮助开发者构建一个安全、稳定、可扩展的电商后端系统。
7. Entity Framework Core ORM数据访问实现
Entity Framework Core(简称 EF Core)是微软推出的一款轻量级、跨平台的ORM框架,广泛应用于.NET Core项目中,尤其适合电商系统的数据库操作管理。它通过面向对象的方式,简化了数据库交互逻辑,提高了开发效率和代码可维护性。本章将从EF Core的基础概念入手,逐步深入到数据操作、查询优化以及在商城系统中的实际应用。
7.1 EF Core的基本概念与使用方式
7.1.1 数据上下文(DbContext)的定义
EF Core 的核心是 DbContext 类,它是与数据库交互的入口。 DbContext 负责管理实体类的生命周期、数据库连接、事务控制以及数据变更的跟踪。
示例代码:定义一个商城系统的数据上下文类
public class MallContext : DbContext
{
public MallContext(DbContextOptions<MallContext> options) : base(options) { }
public DbSet<Product> Products { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 可在此处定义实体与数据库表的映射关系
modelBuilder.Entity<Product>().ToTable("Products");
modelBuilder.Entity<Order>().ToTable("Orders");
modelBuilder.Entity<User>().ToTable("Users");
}
}
参数说明 :
-DbSet<T>表示对应数据库表的实体集合。
-OnModelCreating方法用于配置实体模型与数据库表之间的映射关系。
-DbContextOptions提供数据库连接信息,通常在Startup.cs或Program.cs中配置。
7.1.2 实体类与数据库表的映射关系
每个实体类对应数据库中的一张表,通过 EF Core 的 Fluent API 或数据注解(Data Annotations)来定义字段与列之间的映射。
示例:使用数据注解定义商品实体类
[Table("Products")]
public class Product
{
[Key]
public int ProductId { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
[Column(TypeName = "decimal(18,2)")]
public decimal Price { get; set; }
public int Stock { get; set; }
}
字段说明 :
-[Table("Products")]:指定该实体映射到Products表。
-[Key]:标识主键。
-[Required]:字段不可为空。
-[Column]:指定字段类型和精度。
7.2 数据操作与查询优化
7.2.1 增删改查操作的实现
EF Core 提供了统一的 API 实现数据库的基本操作(CRUD)。
示例:添加一个商品到数据库
var newProduct = new Product
{
Name = "智能手机",
Price = 2999.99m,
Stock = 50
};
_context.Products.Add(newProduct);
await _context.SaveChangesAsync();
说明 :
-Add():将实体加入上下文,准备插入。
-SaveChangesAsync():异步提交更改到数据库。
示例:删除商品
var product = await _context.Products.FindAsync(productId);
if (product != null)
{
_context.Products.Remove(product);
await _context.SaveChangesAsync();
}
7.2.2 LINQ查询与异步处理
EF Core 支持使用 LINQ(Language Integrated Query)进行查询,且推荐使用异步方法以提升性能。
示例:查询价格大于 2000 的商品
var expensiveProducts = await _context.Products
.Where(p => p.Price > 2000)
.ToListAsync();
说明 :
-Where():筛选条件。
-ToListAsync():将查询结果转换为列表,支持异步。
7.2.3 查询性能优化技巧
EF Core 提供多种优化方式,以下是几个常见技巧:
| 优化方式 | 描述 |
|---|---|
使用 AsNoTracking() |
禁止实体变更追踪,适用于只读查询 |
| 显式加载(Eager Loading) | 使用 .Include() 预加载关联数据 |
| 分页查询 | 使用 .Skip() 和 .Take() 实现分页 |
| 缓存查询结果 | 结合 MemoryCache 缓存频繁查询结果 |
示例:显式加载订单及其用户信息
var order = await _context.Orders
.Include(o => o.User)
.FirstOrDefaultAsync(o => o.OrderId == orderId);
7.3 EF Core在商城系统中的实际应用
7.3.1 商品库存管理的数据操作设计
在商城系统中,库存管理需要频繁更新商品数量。EF Core 提供事务支持以确保操作一致性。
示例:减少商品库存
using (var transaction = await _context.Database.BeginTransactionAsync())
{
try
{
var product = await _context.Products
.FirstOrDefaultAsync(p => p.ProductId == productId);
if (product != null && product.Stock >= quantity)
{
product.Stock -= quantity;
await _context.SaveChangesAsync();
await transaction.CommitAsync();
}
else
{
await transaction.RollbackAsync();
throw new InvalidOperationException("库存不足");
}
}
catch (Exception ex)
{
await transaction.RollbackAsync();
// 日志记录异常
}
}
7.3.2 用户订单信息的持久化处理
订单系统通常涉及多个表的关联,如订单头、订单明细等。EF Core 支持批量插入和关联保存。
示例:保存订单与订单明细
var order = new Order
{
UserId = userId,
OrderDate = DateTime.Now,
OrderDetails = new List<OrderDetail>
{
new OrderDetail { ProductId = 1, Quantity = 2 },
new OrderDetail { ProductId = 2, Quantity = 1 }
}
};
_context.Orders.Add(order);
await _context.SaveChangesAsync();
说明 :
- EF Core 会自动将OrderDetails插入到对应的表中,并维护外键关系。
7.3.3 多数据库支持与迁移管理
EF Core 支持多种数据库(SQL Server、MySQL、PostgreSQL、SQLite等),并通过迁移(Migration)机制管理数据库结构变更。
步骤1:添加迁移
dotnet ef migrations add InitialCreate
步骤2:更新数据库
dotnet ef database update
步骤3:切换数据库(以MySQL为例)
在 Program.cs 中配置:
builder.Services.AddDbContext<MallContext>(options =>
options.UseMySql(
builder.Configuration.GetConnectionString("DefaultConnection"),
ServerVersion.AutoDetect(builder.Configuration.GetConnectionString("DefaultConnection"))
));
说明 :
- 通过UseMySql替换为 MySQL 数据库。
- EF Core 会根据数据库类型生成对应的 SQL 语句。
本章介绍了 EF Core 的基本概念、数据操作方式、查询优化技巧及其在电商系统中的典型应用场景。下一章将深入探讨 ASP.NET Core 中的身份认证与授权机制,进一步完善商城系统的功能完整性。
简介:ShopOnline.DotNetCore3是一个基于.NET Core 3.x构建的完整电子商务平台示例,展示了使用ASP.NET Core MVC、Razor Pages、依赖注入、中间件和EF Core开发高性能跨平台Web应用的全过程。项目包含用户注册、商品浏览、购物车与订单处理等核心电商功能,适合学习Web开发最佳实践与现代Web技术栈的整合应用。
更多推荐




所有评论(0)