好的,我们将根据之前的内容,专注于实现应用层的 CQRS(命令查询职责分离)模式。CQRS 是一种架构模式,它将读取操作与写入操作分离,通常使用不同的模型来处理命令和查询。我们将使用 MediatR 库来简化 CQRS 的实现,并确保每个部分都清晰明了。
- 安装 MediatR
首先,在 MyCc.Application 和 MyCc.WebApi 项目中安装 MediatR 包:
dotnet add package MediatR
dotnet add package MediatR.Extensions.Microsoft.DependencyInjection
- 创建命令和查询接口
在 MyCc.Application.Common.Interfaces 文件夹中创建命令和查询接口。
ICommand.cs
namespace MyCc.Application.Common.Interfaces
{
public interface ICommand
{
}
}
IQuery.cs
namespace MyCc.Application.Common.Interfaces
{
public interface IQuery<T>
{
}
}
- 创建命令类
在 MyCc.Application.BOMHeaders.Commands 文件夹中创建命令类。
CreateBOMHeaderCommand.cs
using MyCc.Application.Common.Interfaces;
using MyCc.Application.Dtos;
namespace MyCc.Application.BOMHeaders.Commands
{
public class CreateBOMHeaderCommand : ICommand
{
public BOMHeaderDto BOMHeader { get; set; }
}
}
UpdateBOMHeaderCommand.cs
using MyCc.Application.Common.Interfaces;
using MyCc.Application.Dtos;
namespace MyCc.Application.BOMHeaders.Commands
{
public class UpdateBOMHeaderCommand : ICommand
{
public int BOMID { get; set; }
public BOMHeaderDto BOMHeader { get; set; }
}
}
DeleteBOMHeaderCommand.cs
using MyCc.Application.Common.Interfaces;
namespace MyCc.Application.BOMHeaders.Commands
{
public class DeleteBOMHeaderCommand : ICommand
{
public int BOMID { get; set; }
}
}
- 创建查询类
在 MyCc.Application.BOMHeaders.Queries 文件夹中创建查询类。
GetBOMHeadersQuery.cs
using MyCc.Application.Common.Interfaces;
using MyCc.Application.Dtos;
using System.Collections.Generic;
namespace MyCc.Application.BOMHeaders.Queries
{
public class GetBOMHeadersQuery : IQuery<IEnumerable<BOMHeaderDto>>
{
}
}
GetBOMHeaderByIdQuery.cs
using MyCc.Application.Common.Interfaces;
using MyCc.Application.Dtos;
namespace MyCc.Application.BOMHeaders.Queries
{
public class GetBOMHeaderByIdQuery : IQuery<BOMHeaderDto>
{
public int BOMID { get; set; }
}
}
- 创建命令处理器
在 MyCc.Application.BOMHeaders.CommandHandlers 文件夹中创建命令处理器。
CreateBOMHeaderCommandHandler.cs
using MediatR;
using MyCc.Application.BOMHeaders.Commands;
using MyCc.Application.Services;
using System.Threading;
using System.Threading.Tasks;
namespace MyCc.Application.BOMHeaders.CommandHandlers
{
public class CreateBOMHeaderCommandHandler : IRequestHandler<CreateBOMHeaderCommand, BOMHeaderDto>
{
private readonly BOMHeaderService _bomHeaderService;
public CreateBOMHeaderCommandHandler(BOMHeaderService bomHeaderService)
{
_bomHeaderService = bomHeaderService;
}
public async Task<BOMHeaderDto> Handle(CreateBOMHeaderCommand request, CancellationToken cancellationToken)
{
return await _bomHeaderService.CreateAsync(request.BOMHeader);
}
}
}
UpdateBOMHeaderCommandHandler.cs
using MediatR;
using MyCc.Application.BOMHeaders.Commands;
using MyCc.Application.Services;
using System.Threading;
using System.Threading.Tasks;
namespace MyCc.Application.BOMHeaders.CommandHandlers
{
public class UpdateBOMHeaderCommandHandler : IRequestHandler<UpdateBOMHeaderCommand, bool>
{
private readonly BOMHeaderService _bomHeaderService;
public UpdateBOMHeaderCommandHandler(BOMHeaderService bomHeaderService)
{
_bomHeaderService = bomHeaderService;
}
public async Task<bool> Handle(UpdateBOMHeaderCommand request, CancellationToken cancellationToken)
{
return await _bomHeaderService.UpdateAsync(request.BOMID, request.BOMHeader);
}
}
}
DeleteBOMHeaderCommandHandler.cs
using MediatR;
using MyCc.Application.BOMHeaders.Commands;
using MyCc.Application.Services;
using System.Threading;
using System.Threading.Tasks;
namespace MyCc.Application.BOMHeaders.CommandHandlers
{
public class DeleteBOMHeaderCommandHandler : IRequestHandler<DeleteBOMHeaderCommand, bool>
{
private readonly BOMHeaderService _bomHeaderService;
public DeleteBOMHeaderCommandHandler(BOMHeaderService bomHeaderService)
{
_bomHeaderService = bomHeaderService;
}
public async Task<bool> Handle(DeleteBOMHeaderCommand request, CancellationToken cancellationToken)
{
return await _bomHeaderService.DeleteAsync(request.BOMID);
}
}
}
- 创建查询处理器
在 MyCc.Application.BOMHeaders.QueryHandlers 文件夹中创建查询处理器。
GetBOMHeadersQueryHandler.cs
using MediatR;
using MyCc.Application.BOMHeaders.Queries;
using MyCc.Application.Services;
using System.Threading;
using System.Threading.Tasks;
namespace MyCc.Application.BOMHeaders.QueryHandlers
{
public class GetBOMHeadersQueryHandler : IRequestHandler<GetBOMHeadersQuery, IEnumerable<BOMHeaderDto>>
{
private readonly BOMHeaderService _bomHeaderService;
public GetBOMHeadersQueryHandler(BOMHeaderService bomHeaderService)
{
_bomHeaderService = bomHeaderService;
}
public async Task<IEnumerable<BOMHeaderDto>> Handle(GetBOMHeadersQuery request, CancellationToken cancellationToken)
{
return await _bomHeaderService.GetAllAsync();
}
}
}
GetBOMHeaderByIdQueryHandler.cs
using MediatR;
using MyCc.Application.BOMHeaders.Queries;
using MyCc.Application.Services;
using System.Threading;
using System.Threading.Tasks;
namespace MyCc.Application.BOMHeaders.QueryHandlers
{
public class GetBOMHeaderByIdQueryHandler : IRequestHandler<GetBOMHeaderByIdQuery, BOMHeaderDto>
{
private readonly BOMHeaderService _bomHeaderService;
public GetBOMHeaderByIdQueryHandler(BOMHeaderService bomHeaderService)
{
_bomHeaderService = bomHeaderService;
}
public async Task<BOMHeaderDto> Handle(GetBOMHeaderByIdQuery request, CancellationToken cancellationToken)
{
return await _bomHeaderService.GetByIdAsync(request.BOMID);
}
}
}
- 修改控制器以使用 MediatR
在 MyCc.WebApi.Controllers 文件夹中修改 BOMHeadersController 以使用 MediatR 发送命令和查询。
BOMHeadersController.cs
using MediatR;
using Microsoft.AspNetCore.Mvc;
using MyCc.Application.BOMHeaders.Commands;
using MyCc.Application.BOMHeaders.Queries;
using System.Threading.Tasks;
namespace MyCc.WebApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class BOMHeadersController : ControllerBase
{
private readonly IMediator _mediator;
public BOMHeadersController(IMediator mediator)
{
_mediator = mediator;
}
// GET: api/BOMHeaders
[HttpGet]
public async Task<ActionResult<IEnumerable<BOMHeaderDto>>> GetBOMHeaders()
{
var query = new GetBOMHeadersQuery();
var result = await _mediator.Send(query);
return Ok(result);
}
// GET: api/BOMHeaders/5
[HttpGet("{id}")]
public async Task<ActionResult<BOMHeaderDto>> GetBOMHeader(int id)
{
var query = new GetBOMHeaderByIdQuery { BOMID = id };
var result = await _mediator.Send(query);
if (result == null)
{
return NotFound();
}
return Ok(result);
}
// POST: api/BOMHeaders
[HttpPost]
public async Task<ActionResult<BOMHeaderDto>> PostBOMHeader([FromBody] BOMHeaderDto dto)
{
var command = new CreateBOMHeaderCommand { BOMHeader = dto };
var result = await _mediator.Send(command);
return CreatedAtAction(nameof(GetBOMHeader), new { id = result.BOMID }, result);
}
// PUT: api/BOMHeaders/5
[HttpPut("{id}")]
public async Task<IActionResult> PutBOMHeader(int id, [FromBody] BOMHeaderDto dto)
{
if (id != dto.BOMID)
{
return BadRequest();
}
var command = new UpdateBOMHeaderCommand { BOMID = id, BOMHeader = dto };
var updated = await _mediator.Send(command);
if (!updated)
{
return NotFound();
}
return NoContent();
}
// DELETE: api/BOMHeaders/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteBOMHeader(int id)
{
var command = new DeleteBOMHeaderCommand { BOMID = id };
var deleted = await _mediator.Send(command);
if (!deleted)
{
return NotFound();
}
return NoContent();
}
}
}
- 注册 MediatR 和处理器
在 MyCc.WebApi 项目的 Startup.cs 中注册 MediatR 和所有命令、查询处理器。
Startup.cs
using MediatR;
using MyCc.Application.BOMHeaders.CommandHandlers;
using MyCc.Application.BOMHeaders.QueryHandlers;
using MyCc.Application.Services;
using MyCc.Domain.Repositories;
using MyCc.Infrastructure.Persistence;
using MyCc.Infrastructure.Repositories;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace MyCc.WebApi
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// 添加数据库上下文
services.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
// 注册仓储接口和实现
services.AddScoped<IBOMHeaderRepository, BOMHeaderRepository>();
// 注册应用服务
services.AddScoped<BOMHeaderService>();
// 添加 MediatR 和所有命令、查询处理器
services.AddMediatR(typeof(Startup));
// 添加 MVC 和 API 支持
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
- 总结
通过上述步骤,我们已经成功地在 MyCc 项目的应用层实现了 CQRS 模式。具体来说:
- 命令:用于执行写入操作(如创建、更新、删除),并通过 ICommand 接口定义。
- 查询:用于执行读取操作(如获取列表、获取单个实体),并通过 IQuery
接口定义。 - 命令处理器:负责处理命令并调用相应的应用服务。
- 查询处理器:负责处理查询并返回数据。
- 控制器:通过 MediatR 发送命令和查询,而不是直接调用应用服务。
- 依赖注入:通过 Startup.cs 注册 MediatR 和所有命令、查询处理器。
这种结构使得代码更加模块化和易于维护,同时也符合清洁架构和 DDD 的最佳实践。你可以根据项目的具体需求继续扩展和完善各个层次的功能。
如果有更多具体的需求或问题,请随时告知!
好的,接下来我们将继续完善 MyCc 项目,确保其更加健壮、易于维护,并且符合最佳实践。在这一部分中,我们将添加一些常见的功能和模式,如 事件发布/订阅机制、验证 和 全局异常处理。这些功能将进一步增强系统的可靠性和用户体验。
- 添加事件发布/订阅机制
事件驱动架构(Event-Driven Architecture, EDA)是一种设计模式,它通过事件来解耦系统中的不同组件。我们可以使用 MediatR 来实现领域事件的发布和订阅,从而确保领域逻辑与外部操作(如通知、日志记录等)分离。
14.1 定义领域事件
在 MyCc.Domain.Events 文件夹中创建领域事件类。领域事件通常表示领域模型中发生的重要业务事件。
BOMHeaderCreatedEvent.cs
namespace MyCc.Domain.Events
{
public class BOMHeaderCreatedEvent
{
public int BOMID { get; set; }
public int ProductID { get; set; }
public string Type { get; set; }
public string Usage { get; set; }
public DateTime CreatedAt { get; set; }
public string Status { get; set; }
}
}
BOMHeaderUpdatedEvent.cs
namespace MyCc.Domain.Events
{
public class BOMHeaderUpdatedEvent
{
public int BOMID { get; set; }
public string NewStatus { get; set; }
}
}
BOMHeaderDeletedEvent.cs
namespace MyCc.Domain.Events
{
public class BOMHeaderDeletedEvent
{
public int BOMID { get; set; }
}
}
14.2 修改领域模型以发布事件
我们需要在领域模型中添加事件发布逻辑。每次领域对象发生变化时,都会触发相应的领域事件。我们可以通过一个 DomainEvents 集合来存储这些事件,并在适当的时机发布它们。
BOMHeader.cs
using MyCc.Domain.Events;
using System.Collections.Generic;
using System.Linq;
namespace MyCc.Domain.Entities
{
public class BOMHeader
{
public int BOMID { get; private set; }
public int ProductID { get; private set; }
public string Type { get; private set; }
public string Usage { get; private set; }
public DateTime CreatedAt { get; private set; }
public string Status { get; private set; }
// 现有的构造函数和其他方法...
private List<IDomainEvent> _domainEvents = new List<IDomainEvent>();
public IReadOnlyCollection<IDomainEvent> DomainEvents => _domainEvents.AsReadOnly();
public void AddDomainEvent(IDomainEvent domainEvent)
{
_domainEvents.Add(domainEvent);
}
public void RemoveDomainEvent(IDomainEvent domainEvent)
{
_domainEvents.Remove(domainEvent);
}
public void ClearDomainEvents()
{
_domainEvents.Clear();
}
public void PublishCreatedEvent()
{
var eventToPublish = new BOMHeaderCreatedEvent
{
BOMID = this.BOMID,
ProductID = this.ProductID,
Type = this.Type,
Usage = this.Usage,
CreatedAt = this.CreatedAt,
Status = this.Status
};
AddDomainEvent(eventToPublish);
}
public void PublishUpdatedEvent(string newStatus)
{
var eventToPublish = new BOMHeaderUpdatedEvent
{
BOMID = this.BOMID,
NewStatus = newStatus
};
AddDomainEvent(eventToPublish);
}
public void PublishDeletedEvent()
{
var eventToPublish = new BOMHeaderDeletedEvent
{
BOMID = this.BOMID
};
AddDomainEvent(eventToPublish);
}
}
}
14.3 创建事件处理器
事件处理器负责处理领域事件,并执行相应的业务逻辑。我们可以为每个领域事件创建一个处理器,并使用 MediatR 来订阅这些事件。
BOMHeaderCreatedEventHandler.cs
using MediatR;
using MyCc.Domain.Events;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;
namespace MyCc.Application.EventHandlers
{
public class BOMHeaderCreatedEventHandler : INotificationHandler<BOMHeaderCreatedEvent>
{
private readonly ILogger<BOMHeaderCreatedEventHandler> _logger;
public BOMHeaderCreatedEventHandler(ILogger<BOMHeaderCreatedEventHandler> logger)
{
_logger = logger;
}
public async Task Handle(BOMHeaderCreatedEvent notification, CancellationToken cancellationToken)
{
_logger.LogInformation("BOM Header created with ID: {BOMID}", notification.BOMID);
// 可以在这里添加其他业务逻辑,例如发送通知、更新缓存等
}
}
}
BOMHeaderUpdatedEventHandler.cs
using MediatR;
using MyCc.Domain.Events;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;
namespace MyCc.Application.EventHandlers
{
public class BOMHeaderUpdatedEventHandler : INotificationHandler<BOMHeaderUpdatedEvent>
{
private readonly ILogger<BOMHeaderUpdatedEventHandler> _logger;
public BOMHeaderUpdatedEventHandler(ILogger<BOMHeaderUpdatedEventHandler> logger)
{
_logger = logger;
}
public async Task Handle(BOMHeaderUpdatedEvent notification, CancellationToken cancellationToken)
{
_logger.LogInformation("BOM Header updated with ID: {BOMID}, New Status: {NewStatus}", notification.BOMID, notification.NewStatus);
// 可以在这里添加其他业务逻辑,例如发送通知、更新缓存等
}
}
}
BOMHeaderDeletedEventHandler.cs
using MediatR;
using MyCc.Domain.Events;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;
namespace MyCc.Application.EventHandlers
{
public class BOMHeaderDeletedEventHandler : INotificationHandler<BOMHeaderDeletedEvent>
{
private readonly ILogger<BOMHeaderDeletedEventHandler> _logger;
public BOMHeaderDeletedEventHandler(ILogger<BOMHeaderDeletedEventHandler> logger)
{
_logger = logger;
}
public async Task Handle(BOMHeaderDeletedEvent notification, CancellationToken cancellationToken)
{
_logger.LogInformation("BOM Header deleted with ID: {BOMID}", notification.BOMID);
// 可以在这里添加其他业务逻辑,例如发送通知、更新缓存等
}
}
}
14.4 修改仓储实现以处理事件
在 BOMHeaderRepository 中,我们需要确保在保存实体时发布所有未处理的领域事件。我们可以通过 MediatR 的 Publish 方法来发布这些事件。
BOMHeaderRepository.cs
using MyCc.Domain.Entities;
using MyCc.Domain.Events;
using MyCc.Domain.Repositories;
using MyCc.Infrastructure.Persistence;
using MediatR;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MyCc.Infrastructure.Repositories
{
public class BOMHeaderRepository : IBOMHeaderRepository
{
private readonly MyDbContext _context;
private readonly IMediator _mediator;
public BOMHeaderRepository(MyDbContext context, IMediator mediator)
{
_context = context;
_mediator = mediator;
}
public async Task<IEnumerable<BOMHeader>> GetAllAsync()
{
return await _context.BOMHeaders.ToListAsync();
}
public async Task<BOMHeader> GetByIdAsync(int id)
{
return await _context.BOMHeaders.FindAsync(id);
}
public async Task AddAsync(BOMHeader bomHeader)
{
await _context.BOMHeaders.AddAsync(bomHeader);
bomHeader.PublishCreatedEvent();
await _mediator.Publish(new DomainEventsNotification(bomHeader.DomainEvents), CancellationToken.None);
bomHeader.ClearDomainEvents();
await _context.SaveChangesAsync();
}
public async Task UpdateAsync(BOMHeader bomHeader)
{
_context.Entry(bomHeader).State = EntityState.Modified;
bomHeader.PublishUpdatedEvent(bomHeader.Status);
await _mediator.Publish(new DomainEventsNotification(bomHeader.DomainEvents), CancellationToken.None);
bomHeader.ClearDomainEvents();
await _context.SaveChangesAsync();
}
public async Task DeleteAsync(int id)
{
var bomHeader = await _context.BOMHeaders.FindAsync(id);
if (bomHeader != null)
{
bomHeader.PublishDeletedEvent();
await _mediator.Publish(new DomainEventsNotification(bomHeader.DomainEvents), CancellationToken.None);
_context.BOMHeaders.Remove(bomHeader);
await _context.SaveChangesAsync();
}
}
}
}
DomainEventsNotification.cs
为了简化事件的发布,我们可以创建一个通用的通知类 DomainEventsNotification,它将包含所有需要发布的领域事件。
using MediatR;
using System.Collections.Generic;
namespace MyCc.Application.Common.Notifications
{
public class DomainEventsNotification : INotification
{
public IEnumerable<IDomainEvent> DomainEvents { get; }
public DomainEventsNotification(IEnumerable<IDomainEvent> domainEvents)
{
DomainEvents = domainEvents;
}
}
}
14.5 注册事件处理器
在 Startup.cs 中注册所有事件处理器,以便 MediatR 可以自动发现并订阅这些处理器。
Startup.cs
using MediatR;
using MyCc.Application.EventHandlers;
using MyCc.Application.BOMHeaders.CommandHandlers;
using MyCc.Application.BOMHeaders.QueryHandlers;
using MyCc.Application.Services;
using MyCc.Domain.Repositories;
using MyCc.Infrastructure.Persistence;
using MyCc.Infrastructure.Repositories;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace MyCc.WebApi
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// 添加数据库上下文
services.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
// 注册仓储接口和实现
services.AddScoped<IBOMHeaderRepository, BOMHeaderRepository>();
// 注册应用服务
services.AddScoped<BOMHeaderService>();
// 添加 MediatR 和所有命令、查询处理器
services.AddMediatR(typeof(Startup));
// 添加 MVC 和 API 支持
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
- 添加验证
为了确保输入数据的有效性,我们可以使用 FluentValidation 库来添加验证逻辑。FluentValidation 是一个流行的库,用于定义复杂的验证规则。
15.1 安装 FluentValidation
在 MyCc.Application 项目中安装 FluentValidation 包:
dotnet add package FluentValidation.AspNetCore
15.2 创建验证规则
在 MyCc.Application.BOMHeaders.Validators 文件夹中创建验证规则类。
BOMHeaderDtoValidator.cs
using FluentValidation;
using MyCc.Application.Dtos;
namespace MyCc.Application.BOMHeaders.Validators
{
public class BOMHeaderDtoValidator : AbstractValidator<BOMHeaderDto>
{
public BOMHeaderDtoValidator()
{
RuleFor(x => x.ProductID).GreaterThan(0).WithMessage("ProductID must be greater than 0.");
RuleFor(x => x.Type).NotEmpty().WithMessage("Type is required.");
RuleFor(x => x.Usage).NotEmpty().WithMessage("Usage is required.");
RuleFor(x => x.Status).NotEmpty().WithMessage("Status is required.");
}
}
}
15.3 注册验证器
在 Startup.cs 中注册 FluentValidation 验证器。
Startup.cs
using FluentValidation;
using FluentValidation.AspNetCore;
using MyCc.Application.BOMHeaders.Validators;
public void ConfigureServices(IServiceCollection services)
{
// 其他配置...
// 添加 FluentValidation
services.AddFluentValidationAutoValidation();
services.AddFluentValidationClientsideAdapters();
services.AddScoped<IValidator<BOMHeaderDto>, BOMHeaderDtoValidator>();
// 其他依赖注入...
}
- 添加全局异常处理
为了统一处理应用程序中的异常,我们可以在 MyCc.WebApi 项目中添加全局异常处理中间件。这将确保所有未捕获的异常都能被正确处理,并返回友好的错误信息给客户端。
16.1 创建异常处理中间件
在 MyCc.WebApi.Middleware 文件夹中创建 ExceptionMiddleware.cs 文件。
ExceptionMiddleware.cs
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Net;
using System.Threading.Tasks;
namespace MyCc.WebApi.Middleware
{
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 InvokeAsync(HttpContext httpContext)
{
try
{
await _next(httpContext);
}
catch (Exception ex)
{
_logger.LogError($"Something went wrong: {ex.Message}");
await HandleExceptionAsync(httpContext, ex);
}
}
private Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return context.Response.WriteAsync(new
{
StatusCode = context.Response.StatusCode,
Message = "Internal Server Error from the custom middleware."
}.ToString());
}
}
}
16.2 使用异常处理中间件
在 Startup.cs 中使用异常处理中间件。
Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseMiddleware<ExceptionMiddleware>();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
- 总结
通过上述步骤,我们进一步增强了 MyCc 项目的功能和结构:
- 事件发布/订阅机制:通过领域事件和 MediatR 实现了事件驱动架构,确保领域逻辑与外部操作分离。
- 验证:使用 FluentValidation 添加了输入验证,确保传入的数据符合业务规则。
- 全局异常处理:通过自定义中间件实现了全局异常处理,确保所有未捕获的异常都能被正确处理并返回友好的错误信息。
这种结构不仅提高了代码的可维护性和可测试性,还使得系统更加模块化和易于扩展。你可以根据项目的具体需求继续扩展和完善各个层次的功能。
如果有更多具体的需求或问题,请随时告知!
好的,我们将为 BOMHeaderService 补充代码。这个服务将作为应用层的一部分,负责协调领域逻辑和基础设施之间的交互。它将使用仓储接口来与数据库进行交互,并确保业务规则得到正确执行。
- 创建 BOMHeaderService
在 MyCc.Application.Services 文件夹中创建 BOMHeaderService.cs 文件。
BOMHeaderService.cs
using MyCc.Application.Dtos;
using MyCc.Domain.Entities;
using MyCc.Domain.Repositories;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MyCc.Application.Services
{
public class BOMHeaderService
{
private readonly IBOMHeaderRepository _bomHeaderRepository;
public BOMHeaderService(IBOMHeaderRepository bomHeaderRepository)
{
_bomHeaderRepository = bomHeaderRepository;
}
/// <summary>
/// 获取所有 BOMHeader 的列表。
/// </summary>
/// <returns>BOMHeaderDto 的集合。</returns>
public async Task<IEnumerable<BOMHeaderDto>> GetAllAsync()
{
var bomHeaders = await _bomHeaderRepository.GetAllAsync();
return bomHeaders.Select(MapToDto);
}
/// <summary>
/// 根据 ID 获取 BOMHeader。
/// </summary>
/// <param name="id">BOMHeader 的 ID。</param>
/// <returns>BOMHeaderDto 或 null(如果未找到)。</returns>
public async Task<BOMHeaderDto> GetByIdAsync(int id)
{
var bomHeader = await _bomHeaderRepository.GetByIdAsync(id);
return bomHeader != null ? MapToDto(bomHeader) : null;
}
/// <summary>
/// 创建新的 BOMHeader。
/// </summary>
/// <param name="dto">包含新 BOMHeader 信息的 DTO。</param>
/// <returns>创建的 BOMHeaderDto。</returns>
public async Task<BOMHeaderDto> CreateAsync(BOMHeaderDto dto)
{
var bomHeader = MapToEntity(dto);
await _bomHeaderRepository.AddAsync(bomHeader);
return MapToDto(bomHeader);
}
/// <summary>
/// 更新现有的 BOMHeader。
/// </summary>
/// <param name="id">要更新的 BOMHeader 的 ID。</param>
/// <param name="dto">包含更新信息的 DTO。</param>
/// <returns>更新是否成功。</returns>
public async Task<bool> UpdateAsync(int id, BOMHeaderDto dto)
{
var existingBomHeader = await _bomHeaderRepository.GetByIdAsync(id);
if (existingBomHeader == null)
{
return false;
}
// 更新实体属性
existingBomHeader.ProductID = dto.ProductID;
existingBomHeader.Type = dto.Type;
existingBomHeader.Usage = dto.Usage;
existingBomHeader.Status = dto.Status;
await _bomHeaderRepository.UpdateAsync(existingBomHeader);
return true;
}
/// <summary>
/// 删除指定 ID 的 BOMHeader。
/// </summary>
/// <param name="id">要删除的 BOMHeader 的 ID。</param>
/// <returns>删除是否成功。</returns>
public async Task<bool> DeleteAsync(int id)
{
var bomHeader = await _bomHeaderRepository.GetByIdAsync(id);
if (bomHeader == null)
{
return false;
}
await _bomHeaderRepository.DeleteAsync(id);
return true;
}
/// <summary>
/// 将 BOMHeader 实体映射为 DTO。
/// </summary>
/// <param name="entity">BOMHeader 实体。</param>
/// <returns>BOMHeaderDto。</returns>
private static BOMHeaderDto MapToDto(BOMHeader entity)
{
return new BOMHeaderDto
{
BOMID = entity.BOMID,
ProductID = entity.ProductID,
Type = entity.Type,
Usage = entity.Usage,
CreatedAt = entity.CreatedAt,
Status = entity.Status
};
}
/// <summary>
/// 将 DTO 映射为 BOMHeader 实体。
/// </summary>
/// <param name="dto">BOMHeaderDto。</param>
/// <returns>BOMHeader 实体。</returns>
private static BOMHeader MapToEntity(BOMHeaderDto dto)
{
return new BOMHeader
{
BOMID = dto.BOMID,
ProductID = dto.ProductID,
Type = dto.Type,
Usage = dto.Usage,
CreatedAt = dto.CreatedAt,
Status = dto.Status
};
}
}
}
- 创建 DTO 类
在 MyCc.Application.Dtos 文件夹中创建 BOMHeaderDto.cs 文件,用于定义数据传输对象(DTO),以便在应用层和外部之间传递数据。
BOMHeaderDto.cs
namespace MyCc.Application.Dtos
{
public class BOMHeaderDto
{
public int BOMID { get; set; }
public int ProductID { get; set; }
public string Type { get; set; }
public string Usage { get; set; }
public DateTime CreatedAt { get; set; }
public string Status { get; set; }
}
}
- 修改仓储接口
确保 IBOMHeaderRepository 接口定义了所有必要的方法。你可以在 MyCc.Domain.Repositories 文件夹中找到该接口。
IBOMHeaderRepository.cs
using MyCc.Domain.Entities;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MyCc.Domain.Repositories
{
public interface IBOMHeaderRepository
{
Task<IEnumerable<BOMHeader>> GetAllAsync();
Task<BOMHeader> GetByIdAsync(int id);
Task AddAsync(BOMHeader bomHeader);
Task UpdateAsync(BOMHeader bomHeader);
Task DeleteAsync(int id);
}
}
- 修改仓储实现
确保 BOMHeaderRepository 实现了 IBOMHeaderRepository 接口中的所有方法。你可以在 MyCc.Infrastructure.Repositories 文件夹中找到该类。
BOMHeaderRepository.cs
using MyCc.Domain.Entities;
using MyCc.Domain.Repositories;
using MyCc.Infrastructure.Persistence;
using MediatR;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MyCc.Infrastructure.Repositories
{
public class BOMHeaderRepository : IBOMHeaderRepository
{
private readonly MyDbContext _context;
private readonly IMediator _mediator;
public BOMHeaderRepository(MyDbContext context, IMediator mediator)
{
_context = context;
_mediator = mediator;
}
public async Task<IEnumerable<BOMHeader>> GetAllAsync()
{
return await _context.BOMHeaders.ToListAsync();
}
public async Task<BOMHeader> GetByIdAsync(int id)
{
return await _context.BOMHeaders.FindAsync(id);
}
public async Task AddAsync(BOMHeader bomHeader)
{
await _context.BOMHeaders.AddAsync(bomHeader);
bomHeader.PublishCreatedEvent();
await _mediator.Publish(new DomainEventsNotification(bomHeader.DomainEvents), CancellationToken.None);
bomHeader.ClearDomainEvents();
await _context.SaveChangesAsync();
}
public async Task UpdateAsync(BOMHeader bomHeader)
{
_context.Entry(bomHeader).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
bomHeader.PublishUpdatedEvent(bomHeader.Status);
await _mediator.Publish(new DomainEventsNotification(bomHeader.DomainEvents), CancellationToken.None);
bomHeader.ClearDomainEvents();
await _context.SaveChangesAsync();
}
public async Task DeleteAsync(int id)
{
var bomHeader = await _context.BOMHeaders.FindAsync(id);
if (bomHeader != null)
{
bomHeader.PublishDeletedEvent();
await _mediator.Publish(new DomainEventsNotification(bomHeader.DomainEvents), CancellationToken.None);
_context.BOMHeaders.Remove(bomHeader);
await _context.SaveChangesAsync();
}
}
}
}
- 注册依赖注入
确保在 Startup.cs 中注册 BOMHeaderService 和其他依赖项。
Startup.cs
using MediatR;
using MyCc.Application.BOMHeaders.CommandHandlers;
using MyCc.Application.BOMHeaders.QueryHandlers;
using MyCc.Application.Services;
using MyCc.Domain.Repositories;
using MyCc.Infrastructure.Persistence;
using MyCc.Infrastructure.Repositories;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace MyCc.WebApi
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// 添加数据库上下文
services.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
// 注册仓储接口和实现
services.AddScoped<IBOMHeaderRepository, BOMHeaderRepository>();
// 注册应用服务
services.AddScoped<BOMHeaderService>();
// 添加 MediatR 和所有命令、查询处理器
services.AddMediatR(typeof(Startup));
// 添加 MVC 和 API 支持
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
- 总结
通过上述步骤,我们已经为 BOMHeaderService 补充了完整的代码:
- BOMHeaderService:负责协调领域逻辑和基础设施之间的交互,提供了获取、创建、更新和删除 BOMHeader 的方法。
- BOMHeaderDto:用于在应用层和外部之间传递数据的 DTO 类。
- IBOMHeaderRepository:定义了仓储接口,确保与数据库的交互是通过接口进行的,便于测试和维护。
- BOMHeaderRepository:实现了 IBOMHeaderRepository 接口,负责与数据库进行实际的交互。
- 依赖注入:确保 BOMHeaderService 和其他依赖项在应用程序启动时被正确注册。
这种结构使得代码更加模块化和易于维护,同时也符合清洁架构和 DDD 的最佳实践。你可以根据项目的具体需求继续扩展和完善各个层次的功能。
如果有更多具体的需求或问题,请随时告知!
标签:BomService,MyCc,bomHeader,Application,BOMHeaders,Bom,using,New,public From: https://www.cnblogs.com/eqycc/p/18599881