如何将Razor组件中重复的通用函数封装为可注入服务?
当然可以!甚至这是解决这类重复代码问题的最佳实践之一
你提到的思路和仓储模式的核心逻辑一致——把通用、重复的逻辑抽离到独立的服务类中,通过依赖注入让需要的Razor组件使用,既能大幅减少代码重复,也方便后续统一维护。
针对你的TrySelect函数,这里分两种场景给出具体实现方案:
场景1:组件状态变量完全一致(所有26个组件都有_isOpen、SelectedText等变量)
这种情况可以用组件基类+服务的组合,彻底消除重复代码:
1. 封装通用逻辑到服务(处理与组件无关的部分)
假设objSearchHotel是全局共享的对象,先把这部分逻辑封装成服务:
// 定义搜索酒店服务的接口(方便后续扩展和测试) public interface ISearchHotelService { object SearchObject { get; set; } } public class SearchHotelService : ISearchHotelService { public object SearchObject { get; set; } } // 定义选择逻辑服务 public interface ISelectionService { void SetCitySearchObject(clsCity item); } public class SelectionService : ISelectionService { private readonly ISearchHotelService _searchHotelService; // 构造函数注入依赖 public SelectionService(ISearchHotelService searchHotelService) { _searchHotelService = searchHotelService; } public void SetCitySearchObject(clsCity item) { // 这部分是所有组件通用的逻辑 _searchHotelService.SearchObject = item; } }
2. 创建组件基类(处理组件状态相关的通用逻辑)
把所有组件共有的状态变量和TrySelect方法放到基类中:
public class BaseCitySelectorComponent : ComponentBase { // 依赖注入选择服务 [Inject] protected ISelectionService SelectionService { get; set; } // 所有组件共有的状态变量 protected bool _isOpen; protected string SelectedText { get; set; } protected int SelectedValue { get; set; } // 可根据实际ID类型调整 // 通用的选择方法 protected void TrySelect(clsCity item) { _isOpen = false; SelectedText = item.Nombre; SelectedValue = item.ID; SelectionService.SetCitySearchObject(item); StateHasChanged(); } }
3. 组件继承基类并使用
所有需要TrySelect功能的组件直接继承基类即可:
public partial class ParisCitySelector : BaseCitySelectorComponent { // 组件特有的UI或逻辑写在这里,无需重复写TrySelect }
场景2:组件状态变量不一致(部分组件的状态变量名或逻辑有差异)
这种情况可以只把完全通用的逻辑封装到服务,组件自己处理状态更新:
1. 注册服务到依赖注入容器
在Program.cs中添加服务注册:
builder.Services.AddScoped<ISelectionService, SelectionService>(); builder.Services.AddScoped<ISearchHotelService, SearchHotelService>();
2. 组件中注入服务并简化方法
每个组件注入服务后,只需要维护自己的状态,通用逻辑交给服务处理:
private readonly ISelectionService _selectionService; // 构造函数注入服务 public YourCustomCitySelector(ISelectionService selectionService) { _selectionService = selectionService; } // 组件自己的状态变量(可能和其他组件名不同) private bool _dropdownOpen; private string _selectedCityName; private Guid _selectedCityId; // 可根据实际ID类型调整 void TrySelect(clsCity item) { // 组件特有的状态更新 _dropdownOpen = false; _selectedCityName = item.Nombre; _selectedCityId = item.ID; // 调用通用服务逻辑 _selectionService.SetCitySearchObject(item); StateHasChanged(); }
为什么这么做?
- 减少重复代码:不用在26个文件中复制粘贴相同的函数,后续修改逻辑只需要改一处
- 单一职责:组件专注于UI渲染和用户交互,服务专注于处理通用业务逻辑
- 可测试性:服务类可以单独写单元测试,无需依赖组件
- 灵活性:后续需要扩展逻辑(比如新增日志、校验),只需要修改服务即可
内容的提问来源于stack exchange,提问作者gonzaloev
相关产品推荐
相关产品推荐

