在 .NET 上实现清洁架构
1.创建Application Core项目
2.创建基础设施项目
3.创建Web Api项目
在本文中,我们将学习 .NET 上的 Clean Architecture 简介。我们将创建 3 个项目(应用程序核心、基础架构和 Web API)。
您可以在此处找到幻灯片。
先决条件:
- 带有 .NET 6 SDK 的 Visual Studio 2022
- SQL Server 数据库
1.创建Application Core项目
创建一个名为“StoreCleanArchitecture”的空白解决方案,并添加一个名为“src”的解决方案文件夹,在其中使用.NET Standard 2.1创建一个“类库项目”(创建src文件夹目录项目)
创建以下文件夹:
安装 AutoMapper.Extensions.Microsoft.DependencyInjection。
创建 DependencyInjection 类。
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
namespace Store.ApplicationCore
{
public static class DependencyInjection
{
public static IServiceCollection AddApplicationCore(this IServiceCollection services)
{
services.AddAutoMapper(Assembly.GetExecutingAssembly());
return services;
}
}
}
在实体文件夹中,创建产品类。
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Store.ApplicationCore.Entities
{
public class Product
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[MaxLength(30)]
public string Name { get; set; }
public string Description { get; set; }
public int Stock { get; set; }
public double Price { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
}
}
在 DTOs 文件夹中,创建 Product 类来指定请求和响应。
using System;
using System.ComponentModel.DataAnnotations;
namespace Store.ApplicationCore.DTOs
{
public class CreateProductRequest
{
[Required]
[StringLength(30, MinimumLength = 3)]
public string Name { get; set; }
[Required]
public string Description { get; set; }
[Required]
[Range(0.01, 1000)]
public double Price { get; set; }
}
public class UpdateProductRequest : CreateProductRequest
{
[Required]
[Range(0, 100)]
public int Stock { get; set; }
}
public class ProductResponse
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int Stock { get; set; }
public double Price { get; set; }
}
}
在 Mappings 文件夹中,创建 GeneralProfile 类。这有助于自动从请求映射到实体,以及从实体映射到响应。
using AutoMapper;
using Store.ApplicationCore.DTOs;
using Store.ApplicationCore.Entities;
namespace Store.ApplicationCore.Mappings
{
public class GeneralProfile : Profile
{
public GeneralProfile()
{
CreateMap<CreateProductRequest, Product>();
CreateMap<Product, ProductResponse>();
}
}
}
在 Interfaces 文件夹中,创建 IProductRepository 接口。在这里我们创建 CRUD 的方法。
using Store.ApplicationCore.DTOs;
using System.Collections.Generic;
namespace Store.ApplicationCore.Interfaces
{
public interface IProductRepository
{
List<ProductResponse> GetProducts();
ProductResponse GetProductById(int productId);
void DeleteProductById(int productId);
ProductResponse CreateProduct(CreateProductRequest request);
ProductResponse UpdateProduct(int productId, UpdateProductRequest request);
}
}
在 Exceptions 文件夹中,创建 NotFoundException 类。
using System;
namespace Store.ApplicationCore.Exceptions
{
public class NotFoundException : Exception
{
}
}
在 Utils 文件夹中,创建 DateUtil 类。
using System;
namespace Store.ApplicationCore.Utils
{
public class DateUtil
{
public static DateTime GetCurrentDate()
{
return TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, TimeZoneInfo.Local);
}
}
}
2.创建基础设施项目
使用 .NET 6 创建一个名为 Store.Infrastructure 的“类库项目”。
创建以下结构:
安装 Microsoft.EntityFrameworkCore.SqlServer。
右键单击 Store.Infrastucture 项目 / 添加 / 项目引用... / 勾选 Store.ApplicationCore / 确定
在 Contexts 文件夹中,创建 StoreContext 类。在这里,我们将 Product 实体添加到 DbSets,以便与数据库的 Products 表进行通信。
using Microsoft.EntityFrameworkCore;
using Store.ApplicationCore.Entities;
namespace Store.Infrastructure.Persistence.Contexts
{
public class StoreContext : DbContext
{
public StoreContext(DbContextOptions<StoreContext> options) : base(options)
{
}
public DbSet<Product> Products { get; set; }
}
}
在 Repositories 文件夹中,创建 ProductRepository 类。
using AutoMapper;
using Store.ApplicationCore.DTOs;
using Store.ApplicationCore.Entities;
using Store.ApplicationCore.Exceptions;
using Store.ApplicationCore.Interfaces;
using Store.ApplicationCore.Utils;
using Store.Infrastructure.Persistence.Contexts;
using System.Collections.Generic;
using System.Linq;
namespace Store.Infrastructure.Persistence.Repositories
{
public class ProductRepository : IProductRepository
{
private readonly StoreContext storeContext;
private readonly IMapper mapper;
public ProductRepository(StoreContext storeContext, IMapper mapper)
{
this.storeContext = storeContext;
this.mapper = mapper;
}
public ProductResponse CreateProduct(CreateProductRequest request)
{
var product = this.mapper.Map<Product>(request);
product.Stock = 0;
product.CreatedAt = product.UpdatedAt = DateUtil.GetCurrentDate();
this.storeContext.Products.Add(product);
this.storeContext.SaveChanges();
return this.mapper.Map<ProductResponse>(product);
}
public void DeleteProductById(int productId)
{
var product = this.storeContext.Products.Find(productId);
if (product != null)
{
this.storeContext.Products.Remove(product);
this.storeContext.SaveChanges();
}
else
{
throw new NotFoundException();
}
}
public ProductResponse GetProductById(int productId)
{
var product = this.storeContext.Products.Find(productId);
if (product != null)
{
return this.mapper.Map<ProductResponse>(product);
}
throw new NotFoundException();
}
public List<ProductResponse> GetProducts()
{
return this.storeContext.Products.Select(p => this.mapper.Map<ProductResponse>(p)).ToList();
}
public ProductResponse UpdateProduct(int productId, UpdateProductRequest request)
{
var product = this.storeContext.Products.Find(productId);
if (product != null)
{
product.Name = request.Name;
product.Description = request.Description;
product.Price = request.Price;
product.Stock = request.Stock;
product.UpdatedAt = DateUtil.GetCurrentDate();
this.storeContext.Products.Update(product);
this.storeContext.SaveChanges();
return this.mapper.Map<ProductResponse>(product);
}
throw new NotFoundException();
}
}
}
在 DependencyInjection 类中,添加以下内容:
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Store.ApplicationCore.Interfaces;
using Store.Infrastructure.Persistence.Contexts;
using Store.Infrastructure.Persistence.Repositories;
namespace Store.Infrastructure
{
public static class DependencyInjection
{
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
{
var defaultConnectionString = configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<StoreContext>(options =>
options.UseSqlServer(defaultConnectionString));
services.AddScoped<IProductRepository, ProductRepository>();
return services;
}
}
}
我们在那里配置数据库上下文并将 IProductRepository 作为 Scoped 添加到服务集合中。
3.创建Web Api项目
使用.NET 6创建一个名为Store.WebApi的“Web Api项目”。
右键单击 Store.WebApi / 设置为启动项目。
在顶部,单击“调试 / 启动(不调试)”。
删除 WeatherForecast 和 WeatherForecastController 文件。
添加对 Store.ApplicationCore 和 Store.Infrastructure 项目的引用。
在 appsettings.json 中添加 SQL Server 连接字符串
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=DemoStore;Trusted_Connection=True;"
}
}
在程序类中,添加应用程序核心和基础设施的扩展。
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Store.ApplicationCore;
using Store.Infrastructure;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddApplicationCore();
builder.Services.AddInfrastructure(builder.Configuration);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthorization();
app.MapControllers();
app.Run();
打开包管理器控制台,选择 Store.Infrastructure 项目作为默认项目。执行Add-Migration InitialCreate -Context StoreContext
。
在 Store.Infrastructure 项目中,创建了一个包含 2 个文件的 Migrations 文件夹。
然后,从包管理器控制台执行Update-Database
。
从控制器中,添加一个名为 ProductsController 的控制器
using Microsoft.AspNetCore.Mvc;
using Store.ApplicationCore.DTOs;
using Store.ApplicationCore.Exceptions;
using Store.ApplicationCore.Interfaces;
using System.Collections.Generic;
namespace Store.WebApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ProductsController : Controller
{
private readonly IProductRepository productRepository;
public ProductsController(IProductRepository productRepository)
{
this.productRepository = productRepository;
}
[HttpGet]
public ActionResult<List<ProductResponse>> GetProducts()
{
return Ok(this.productRepository.GetProducts());
}
[HttpGet("{id}")]
public ActionResult GetProductById(int id)
{
try
{
var product = this.productRepository.GetProductById(id);
return Ok(product);
}
catch (NotFoundException)
{
return NotFound();
}
}
[HttpPost]
public ActionResult Create(CreateProductRequest request)
{
var product = this.productRepository.CreateProduct(request);
return Ok(product);
}
[HttpPut("{id}")]
public ActionResult Update(int id, UpdateProductRequest request)
{
try
{
var product = this.productRepository.UpdateProduct(id, request);
return Ok(product);
}
catch (NotFoundException)
{
return NotFound();
}
}
[HttpDelete("{id}")]
public ActionResult Delete(int id)
{
try
{
this.productRepository.DeleteProductById(id);
return NoContent();
}
catch (NotFoundException)
{
return NotFound();
}
}
}
}
您可以在此处找到源代码。
感谢阅读
非常感谢您的阅读,希望您觉得这篇文章有趣,并希望将来能有所收获。如果您有任何疑问或想法需要讨论,我们很乐意与您合作,共同交流。
鏂囩珷鏉ユ簮锛�https://dev.to/cristofima/implement-clean-architecture-on-net-59eo