New-Bom-BomService
好的,我们将根据之前的内容,专注于实现应用层的 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 的最佳实践。你可以根据项目的具体需求继续扩展和完善各个层次的功能。
如果有更多具体的需求或问题,请随时告知!
 
                    
                     
                    
                 
                    
                 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号