想象一下:你坐在书桌前,啜饮着你最喜欢的饮料(无论是咖啡还是传说中的“显影剂燃料”——含咖啡因的可乐)。您梦想构建一个实时购物应用程序,让 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 使前端用户友好,您可以开始发布。
最后的步骤:
数据库配置:您的 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
在中配置 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 8、SignalR、CQRS with MediatR、Razor Pages 和 T4 模板构建了一个功能齐全的实时购物应用程序。您的应用程序不仅能够实时更新库存,而且还具有干净、可维护的架构,可以像专业人士一样分离职责并处理数据持久性。
如果你喜欢我的文章,请给我一个赞!谢谢