如何在 ASP.NET Core 8 中构建实时购物应用程序

科技   2024-11-19 17:01   上海  


想象一下:你坐在书桌前,啜饮着你最喜欢的饮料(无论是咖啡还是传说中的“显影剂燃料”——含咖啡因的可乐)。您梦想构建一个实时购物应用程序,让 Amazon 看起来像是停滞在 1995 年。您希望它闪闪发光,闪耀着 T4 模板SignalR带有 MediatR 的 CQRS,当然还有闪亮的 Razor Pages UI 的魔力。您需要_速度_。您需要_实时更新_。您需要_可扩展性_。而且您希望在不编写感觉属于中世纪的代码的情况下完成所有操作。

好吧,系好安全带,我的朋友,因为我们即将进行一次实时编码冒险,甚至甘道夫都会说,“如果不编写优雅、现代的 C# 代码,你就(不能)通过。

1. T4 模板:代码生成

谁喜欢一遍又一遍地编写相同的样板代码?没人。这就是 T4 模板存在的原因 — 将您从危险的命运中拯救出来。它们是为您生成代码的魔杖,确保您不必浪费宝贵的脑力来重复自己。

如何挥动你的 T4 魔法

首先,将 T4 模板添加到您的项目中,因为没有人喜欢没有一点魔法的项目。

dotnet add package Microsoft.VisualStudio.TextTemplating

接下来,让我们让代码实际上自行编写。您可以创建一个文件,为您的 Product 模型自动生成存储库接口。.tt

<#@ template language="C#" #>
<#@ output extension=".cs" #>
<#
var entityName = "Product";
#>
namespace ShoppingApp.Data.Repositories
{
public interface I<#= entityName #>Repository
{
<#= entityName #> GetById(int id);
IEnumerable<<#= entityName #>> Find(string searchTerm);
IEnumerable<<#= entityName #>> GetAll();
void Add(<#= entityName #> entity);
void Update(<#= entityName #> entity);
void Delete(int id);
}
}

瞧!您刚刚为自己节省了数小时的盲目编码。您正式成为代码库的向导。

2. SignalR:实时信使

让我们面对现实吧:如果您的购物应用程序没有向用户更新实时库存信息,您是否正在尝试?这就是 SignalR 的用武之地 — 它就像一只实时的咖啡因信鸽。

使用 SignalR,您可以向用户推送实时通知,以便他们始终知道他们最喜欢的产品何时从“售罄”下降到“只剩一个 — 运行!

如何驯服 SignalR 野兽

首先,您需要安装 SignalR 包,否则,您的应用程序将非常不健谈。

dotnet add package Microsoft.AspNetCore.SignalR

然后,创建您的 SignalR Hub,这基本上就像建造一座塔,所有消息鸽子都在这里闲逛。

public class ShoppingHub : Hub  
{
public async Task NotifyStockUpdate(string productId, int newStock)
{
await Clients.All.SendAsync("ReceiveStockUpdate", productId, newStock);
}
}

实时提醒 — 因为谁有时间刷新页面?

您还希望您的前端像八卦邻居一样监听您的集线器

<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.18/signalr.min.js"></script>
<script>
const connection = new signalR.HubConnectionBuilder().withUrl("/shoppingHub").build();
connection.on("ReceiveStockUpdate", function (productId, newStock) {
alert(`Product ${productId} has new stock: ${newStock}`);
});
connection.start().catch(err => console.error(err.toString()));
</script>

现在,您的用户将紧紧盯着他们的屏幕,永远等待那个难以捉摸的“1 个有货”通知。看啊,SignalR 的强大功能。

3. 使用 MediatR 的 CQRS:保持代码整洁

啊,CQRS — 命令查询责任分离。这是将 “I want to ask” 与 “I want to do” 区分开来的完美方式,确保您的代码最终不会变成一团纠结的意大利面条。

MediatR 随时为您提供帮助,因为谁不喜欢一个好的中间人呢?它可以像邮政服务一样处理繁重的调度命令和查询工作 — 如果邮政服务非常高效并且不会丢失包裹。

处理命令:将产品添加到购物车

让我们创建一个将产品添加到购物车的命令。这就是分离的魔力真正开始闪耀的地方。

public class AddProductCommand : IRequest
{
public string ProductId { get; set; }
public int Quantity { get; set; }
}
public class AddProductCommandHandler : IRequestHandler<AddProductCommand>
{
private readonly IProductRepository _productRepository;
public AddProductCommandHandler(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public async Task<Unit> Handle(AddProductCommand request, CancellationToken cancellationToken)
{
var product = await _productRepository.GetByIdAsync(request.ProductId);
product.Quantity -= request.Quantity;
await _productRepository.UpdateAsync(product);
return Unit.Value;
}
}

处理查询:寻找完美的产品

不要只让用户将产品添加到购物车中 - 让他们通过查询_找到_他们想要的产品。

public class FindProductsQuery : IRequest<IEnumerable<Product>>
{
public string SearchTerm { get; set; }
}
public class FindProductsQueryHandler : IRequestHandler<FindProductsQuery, IEnumerable<Product>>
{
private readonly IProductRepository _productRepository;
public FindProductsQueryHandler(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public async Task<IEnumerable<Product>> Handle(FindProductsQuery request, CancellationToken cancellationToken)
{
return await _productRepository.FindAsync(request.SearchTerm);
}
}

但为什么选择 MediatR?

将 MediatR 视为您自己的请求和命令邮政服务。它确保所有内容都路由到正确的处理程序,因此您不必自己管理那些烦人的互连。这里没有丢失的查询。

4. 存储库模式:数据库的最佳朋友

您不会只想随意丢弃数据,对吧?输入 repository pattern (存储库模式)。这就像有一个组织良好的管家,他知道所有东西都存放在哪里,以及当你好心询问时如何获取它。

public interface IProductRepository
{
Task<Product> GetByIdAsync(string id);
Task<IEnumerable<Product>> FindAsync(string searchTerm);
Task<IEnumerable<Product>> GetAllAsync();
Task AddAsync(Product product);
Task UpdateAsync(Product product);
Task DeleteAsync(string id);
}
public class ProductRepository : IProductRepository
{
private readonly ApplicationDbContext _context;
public ProductRepository(ApplicationDbContext context)
{
_context = context;
}
public async Task<Product> GetByIdAsync(string id)
{
return await _context.Products.FindAsync(id);
}
public async Task<IEnumerable<Product>> FindAsync(string searchTerm)
{
return await _context.Products
.Where(p => p.Name.Contains(searchTerm) || p.Description.Contains(searchTerm))
.ToListAsync();
}
// Other methods follow a similar pattern...
}

现在,您的 ProductRepository 已完全准备好应对您向其提出的所有与数据相关的挑战——无论是获取产品、添加新产品,还是删除名称可疑的“Product X”产品。

5. Razor Pages UI:让一切变得漂亮

没有花哨用户界面的购物应用程序是什么?这就像一辆没有油漆的法拉利——当然,它有效,但 pizazz 在哪里?

创建产品列表页面

@page
@model ProductsModel
<h2>Product List</h2>
<input type="text" id="searchInput" placeholder="Search Products" />
<button id="searchBtn">Search</button>
<ul id="productList">
@foreach (var product in Model.Products)
{
<li>@product.Name - @product.Quantity in stock</li>
}
</ul>
<script>
document.getElementById('searchBtn').addEventListener('click', function() {
const searchTerm = document.getElementById('searchInput').value;
fetch(`/api/products/search?term=${searchTerm}`)
.then(response => response.json())
.then(products => {
const list = document.getElementById('productList');
list.innerHTML = '';
products.forEach(product => {
const li = document.createElement('li');
li.textContent = `${product.name} - ${product.quantity} in stock`;
list.appendChild(li);
});
});
});
</script>

有了这个,用户可以搜索产品,查看库存水平,并且可能会冲动地购买比他们需要的更多的东西。最好的资本主义。

6. 将所有内容与实时购物体验捆绑在一起

现在我们已经奠定了基础,是时候将实时购物应用程序的所有部分整合在一起了。借助 SignalR 处理实时通知,CQRS 将命令和查询职责分开,以及 Razor Pages 使前端用户友好,您可以开始发布。

最后的步骤:

  1. 数据库配置:您的 ApplicationDbContext 需要连接到您的应用程序,以便顺利使用 Entity Framework Core

public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Product> Products { get; set; }
}

确保您的 :appsettings.json

{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ShoppingAppDb;Trusted_Connection=True;"
}
}

运行以下命令以设置数据库并应用迁移:

dotnet ef migrations add InitialCreate  
dotnet ef database update
  1. 在中配置 MediatR 和 SignalR:在 ASP.NET Core 中注册您的服务。Program.cs

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddMediatR(typeof(Program).Assembly);
builder.Services.AddSignalR();
builder.Services.AddScoped<IProductRepository, ProductRepository>();
builder.Services.AddRazorPages();
var app = builder.Build();
app.MapRazorPages();
app.MapHub<ShoppingHub>("/shoppingHub");
app.Run();

**测试一下!**设置应用程序后,您现在可以:

  • 通过 Razor Pages UI 搜索产品

  • 使用 CQRS 命令将产品添加到购物车。

  • 使用 SignalR 通知查看实时股票更新

当产品的库存发生变化时,您的用户将收到即时通知(无需刷新页面),让您的应用程序感觉活泼而现代。

您刚刚使用 ASP.NET Core 8SignalRCQRS with MediatRRazor Pages 和 T4 模板构建了一个功能齐全的实时购物应用程序。您的应用程序不仅能够实时更新库存,而且还具有干净、可维护的架构,可以像专业人士一样分离职责并处理数据持久性。

如果你喜欢我的文章,请给我一个赞!谢谢

架构师老卢
资深软件架构师, 分享编程、软件设计经验, 教授前沿技术, 分享技术资源(每天发布电子书),每天进步一点点...
 最新文章