ASP.NET MVC不同ViewModel间函数传参与列表合并实现
1. 先补全服务抽象层,遵循依赖注入规范
你当前两个类是独立的,要跨类调用逻辑不能直接硬编码实例化具体类,先给CommerceService抽离公共调用接口,避免层与层之间的强耦合:
// 抽离CommerceService的公共契约 public interface ICommerceService : IDisposable { // 保留原有泛型方法 DownloadAndOrderItemsViewModel GetDownloadAndOrderItemsViewModel<T>(T model) where T : DownloadAndOrderItems; // 新增专门对外提供下载列表的方法,避免其他服务调用时重复构建ViewModel List<DownloadItem> GetDownloadListForMerge<T>(T model) where T : DownloadAndOrderItems; }
修改CommerceService实现,抽离公共逻辑避免代码重复:
public class CommerceService : ICommerceService { // 原有构造函数、依赖、Dispose逻辑全部保留 public List<DownloadItem> GetDownloadListForMerge<T>(T model) where T : DownloadAndOrderItems { if (model == null) return new List<DownloadItem>(); var downloadList = model.GetDownloadList(); return downloadList ?? new List<DownloadItem>(); } public DownloadAndOrderItemsViewModel GetDownloadAndOrderItemsViewModel<T>(T model) where T : DownloadAndOrderItems { var downloadList = GetDownloadListForMerge(model); if (!downloadList.Any()) return null; return new DownloadAndOrderItemsViewModel { DownloadList = downloadList // 原有其他属性赋值逻辑保持不变 }; } public void Dispose() { // 原有资源释放逻辑保持不变 } }
修改完后在DI注册配置(Startup.cs/Program.cs,根据你用的MVC版本选择)中添加ICommerceService和CommerceService的生命周期注册,和你项目里其他服务的生命周期保持一致即可,一般业务服务用AddScoped。
2. 调整DocumentLibraryViewModelFactory的依赖与方法逻辑
首先通过构造函数注入ICommerceService,再修改私有方法GetDocumentLibraryViewModel的逻辑,完成列表合并:
public class DocumentLibraryViewModelFactory : IDocumentLibraryViewModelFactory { // 原有依赖保留 private readonly IContentLoader _contentLoader; private readonly IDocumentLibraryService _documentLibraryService; private readonly IMapper _mapper; private readonly IDocumentLibraryCategoriesService _categoriesService; // 新增Commerce服务依赖 private readonly ICommerceService _commerceService; // 构造函数新增ICommerceService参数 public DocumentLibraryViewModelFactory( IContentLoader contentLoader, IDocumentLibraryService documentLibraryService, IMapper mapper, IDocumentLibraryCategoriesService categoriesService, ICommerceService commerceService) { _contentLoader = contentLoader; _documentLibraryService = documentLibraryService; _mapper = mapper; _categoriesService = categoriesService; _commerceService = commerceService; } // 原有公共方法逻辑保留,只修改私有方法的参数和内部逻辑 private DocumentLibraryViewModel GetDocumentLibraryViewModel( DocumentLibrary content, HomePage homePage, DocumentLibraryFilterResponse<DocumentLibraryFileMetaData> initialData, DownloadAndOrderItems commerceDataSource = null) // 新增可选参数,不影响原有调用 { // 1. 获取Commerce侧的下载列表 var commerceDownloadItems = _commerceService.GetDownloadListForMerge(commerceDataSource); // 2. 类型对齐:把DownloadItem转成Files属性需要的DocumentLibraryFileMetaData类型 // 你已经注入了IMapper,直接在AutoMapper配置里加两个类型的映射规则即可,不要硬写字段转换 var mappedCommerceFiles = _mapper.Map<List<DocumentLibraryFileMetaData>>(commerceDownloadItems); // 3. 合并两个来源的文件列表,按业务唯一键去重(比如文件ID、文件路径) var mergedFiles = initialData.Contents .Concat(mappedCommerceFiles) .GroupBy(file => file.FileId) // 替换成你项目里实际的文件唯一标识属性 .Select(group => group.First()) .ToList(); // 4. 构建ViewModel时赋值合并后的列表 var viewModel = new DocumentLibraryViewModel { Files = mergedFiles // 原有其他属性赋值逻辑保持不变 }; return viewModel; } }
注意:必须在AutoMapper的Profile配置中补充DownloadItem到DocumentLibraryFileMetaData的映射规则,把两边共通的属性(文件名、访问路径、大小、上传时间等)做对应,差异字段按业务需求补默认值或者做自定义转换即可。
3. 调整上层调用传参
找到你项目中调用GetDocumentLibraryViewModel的公共入口(一般是Factory对外暴露的Build方法,供Controller/ViewComponent调用),给这个公共方法新增一个可选的DownloadAndOrderItems类型参数,上层调用时把当前页面对应的继承了DownloadAndOrderItems的数据源传进去即可,不需要额外查库。
举个修改示例:
// 原来的公共构建方法 // public DocumentLibraryViewModel Build(DocumentLibrary content, HomePage homePage, DocumentLibraryFilterResponse<DocumentLibraryFileMetaData> initialData) // 修改后 public DocumentLibraryViewModel Build( DocumentLibrary content, HomePage homePage, DocumentLibraryFilterResponse<DocumentLibraryFileMetaData> initialData, DownloadAndOrderItems commerceSource = null) { // 原有前置逻辑保持不变 return GetDocumentLibraryViewModel(content, homePage, initialData, commerceSource); }
如果是不需要合并Commerce下载列表的场景,调用时不传最后一个参数即可,完全兼容原有逻辑。
避坑说明
- 绝对不要在Factory内部直接
new CommerceService(),否则会导致CommerceService内部的依赖(数据库上下文、配置、其他服务)无法正常解析,还会引发资源泄漏问题 - 列表合并必须做去重,避免同一份文件在两个数据源都存在时重复展示
- 类型转换统一用已经注入的IMapper处理,不要在方法里逐字段手写转换,后续字段变更时维护成本极高
- 如果两个列表的排序规则有要求,可以在合并后加
OrderBy逻辑,按你业务需要的排序字段(比如上传时间、文件名)排序后再赋值给Files属性
内容的提问来源于stack exchange,提问作者cowefe

