Mapster如何编写带动态参数的表达式?EF Core实体映射遇问题
问题
在项目中使用Mapster将EF Core实体映射到DTO。原本在请求处理器中配置映射可正常工作,但根据Mapster建议,为避免性能问题,TypeAdapterConfig应仅在程序入口处定义一次。
原工作代码:
// original code TypeAdapterConfig<ProductEntity, ProductDto>.NewConfig() .Map(d => d.Available, ProductExpression.AvailableForStoreId(request.ForStoreId)); public static Expression<Func<ProductEntity, bool>> AvailableForStoreId(DefaultIdType storeId) { return s => s.ProductGroups.Any(gp => gp.storeRelProductGroup.Any(r => r.StoreId == storeId)); }
尝试改用MapContext传递动态参数并将配置移至入口时,启动项目立即抛出空引用错误——原因是MapContext.Current在TypeAdapterConfig定义时为空,表达式被提前调用:
// Parameters approach TypeAdapterConfig<ProductEntity, ProductDto>.NewConfig() .Map(d => d.HasPricing, ProductExpression.AvailableForStoreId((int)MapContext.Current.Parameters["storeId"]));
需要解决:如何保留AvailableForStoreId的表达式逻辑,同时按请求提供动态参数storeId,且符合Mapster仅在入口配置一次的要求?
解决方案
方法1:直接在映射配置中使用上下文参数
在入口配置时,通过Mapster提供的上下文参数ctx直接引用动态值,避免提前调用表达式生成方法:
// 程序入口处的映射配置 TypeAdapterConfig<ProductEntity, ProductDto>.NewConfig() .Map(d => d.HasPricing, (src, dest, ctx) => src.ProductGroups.Any(gp => gp.storeRelProductGroup.Any(r => r.StoreId == (DefaultIdType)ctx.Parameters["storeId"])));
请求处理时传递参数:
var dto = productEntity.Adapt<ProductDto>(new MapContext { Parameters = { ["storeId"] = request.ForStoreId } });
方法2:修改表达式生成方法支持延迟参数解析
调整AvailableForStoreId方法,让它接受一个代表storeId的表达式参数,实现延迟解析上下文值:
// 修改后的表达式生成方法 public static Expression<Func<ProductEntity, bool>> AvailableForStoreId(Expression<Func<DefaultIdType>> storeIdExpr) { var productParam = Expression.Parameter(typeof(ProductEntity), "s"); var storeIdValue = storeIdExpr.Body; // 拼接Any嵌套的表达式逻辑 var storeRelCheck = Expression.Lambda<Func<StoreRelProductGroup, bool>>( Expression.Equal( Expression.Property(Expression.Parameter(typeof(StoreRelProductGroup), "r"), nameof(StoreRelProductGroup.StoreId)), storeIdValue ), Expression.Parameter(typeof(StoreRelProductGroup), "r") ); var groupCheck = Expression.Lambda<Func<ProductGroup, bool>>( Expression.Call( typeof(Enumerable), "Any", new[] { typeof(StoreRelProductGroup) }, Expression.Property(Expression.Parameter(typeof(ProductGroup), "gp"), nameof(ProductGroup.storeRelProductGroup)), storeRelCheck ), Expression.Parameter(typeof(ProductGroup), "gp") ); var finalExpr = Expression.Lambda<Func<ProductEntity, bool>>( Expression.Call( typeof(Enumerable), "Any", new[] { typeof(ProductGroup) }, Expression.Property(productParam, nameof(ProductEntity.ProductGroups)), groupCheck ), productParam ); return finalExpr; }
入口配置时传入上下文参数的表达式:
// 程序入口处的映射配置 TypeAdapterConfig<ProductEntity, ProductDto>.NewConfig() .Map(d => d.HasPricing, AvailableForStoreId(() => (DefaultIdType)MapContext.Current.Parameters["storeId"]));
请求处理时传递参数:
var dto = productEntity.Adapt<ProductDto>(new MapContext { Parameters = { ["storeId"] = request.ForStoreId } });
方法3:简化的上下文参数传递(推荐)
使用Mapster的简化语法,在调用时直接注入参数:
// 入口配置 TypeAdapterConfig<ProductEntity, ProductDto>.NewConfig() .Map(d => d.HasPricing, (src, ctx) => src.ProductGroups.Any(gp => gp.storeRelProductGroup.Any(r => r.StoreId == (DefaultIdType)ctx.Parameters["storeId"])));
调用映射时:
var dto = productEntity.Adapt<ProductDto>(ctx => ctx.Parameters["storeId"] = request.ForStoreId);
内容的提问来源于stack exchange,提问作者C TC
相关产品推荐
相关产品推荐

