
[英]JSON.NET - Merge and ContainsKey don't work in v11.0.1 -> v13.0.2
[英]Write model properties not mapped when using AutoMapper v11.0.1
我正在开发一个 ASP.NET 核心项目,在该项目中我定义了一些Profile
类,这些类用于通过我的 Startup class 中的此语句配置 AutoMapper: services.AddAutoMapper(typeof(CommissionRulesReadModelProfile).Assembly);
.
我目前正在尝试更新以下依赖项:
我发现更新到这些版本后,我的写入模型的某些属性没有映射到相应的域实体。
在我的项目中,我有以下域实体和相应的写入模型:
写入模型使用可公开访问的{ get; init; }
{ get; init; }
{ get; init; }
属性,并且域实体都通过它们的构造函数设置了它们的属性。
我使用以下代码从应用层模型到域的 map:
public class CommissionRulesWriteModelProfile : Profile
{
public CommissionRulesWriteModelProfile()
{
ShouldMapProperty = p => (p.GetMethod!.IsPublic || p.GetMethod.IsAssembly)
&& p.Name != nameof(Entity.Id)
&& p.Name != nameof(Entity.DomainEvents);
// ...
MapUnitCommissionRuleset();
// ...
}
private void MapUnitCommissionRuleset()
{
var unitCommissionRulesetMap =
CreateMap<UnitCommissionRulesetWriteModel, UnitCommissionRuleset>();
unitCommissionRulesetMap.MapPerformanceMetricScoreRule(
src => src.AverageCsiScoreRange,
dest => dest.AverageCsiScoreRule,
thresholds => new AverageCsiScoreRule(thresholds));
// ...
CreateMap<PointsThresholdWriteModel, UnitCommissionPointsThreshold>();
}
}
public static class MappingExtensions
{
/// <summary>
/// Maps a performance metric rule within a commission ruleset write model to the domain-layer rule.
/// </summary>
public static void MapPerformanceMetricScoreRule<TRange, TRule>(
this IMappingExpression<UnitCommissionRulesetWriteModel, UnitCommissionRuleset> map,
Func<UnitCommissionRulesetWriteModel, TRange> rangeSelector,
Expression<Func<UnitCommissionRuleset, TRule>> ruleSelector,
Func<ICollection<UnitCommissionPointsThreshold>, TRule> constructRuleFromThresholds) where TRule : class
{
TRule MapRule(
UnitCommissionRulesetWriteModel src,
UnitCommissionRuleset dest,
TRule rule,
ResolutionContext ctx)
{
var range = rangeSelector(src);
if (range is null)
return null;
var thresholds = ctx.Mapper.Map<List<UnitCommissionPointsThreshold>>(range);
return constructRuleFromThresholds(thresholds);
}
map.ForMember(ruleSelector, opt => opt.MapFrom(MapRule));
}
}
在 AutoMapper v10 中,这可以正常工作。 In v11 I find that the UnitCommissionRuleset.AverageCsiScoreRule
is null even when the corresponding write model is populated, and that the MapRule
local function within MapPerformanceMetricScoreRule
is never invoked.
以下 .NET 6 控制台应用程序在使用 AutoMapper v10.0.0 和 AutoMapper.Extensions.Microsoft.DependencyInjection v8.0.1 时执行没有问题,但在分别升级到 v11.0.1 和 v11.0.0 时无法执行 map。 该项目还依赖于Microsoft.Extensions.DependencyInjection
v6.0.0。
如果此代码可以与 AutoMapper v11 一起使用,我希望该解决方案也适用于我的 ASP.NET 核心项目。
using System.Linq.Expressions;
using AutoMapper;
using Microsoft.Extensions.DependencyInjection;
public class Program
{
public static void Main(string[] args)
{
var serviceProvider = new ServiceCollection()
.AddAutoMapper(typeof(ExampleProfile).Assembly)
.BuildServiceProvider();
var mapper = serviceProvider.GetRequiredService<IMapper>();
var writeModel = CreateExampleWriteModel();
var unitCommissionRuleset = mapper.Map<UnitCommissionRuleset>(writeModel);
if (unitCommissionRuleset.AverageCsiScoreRule is null)
throw new Exception("Average CSI Score Rule property was not mapped");
}
private static UnitCommissionRulesetWriteModel CreateExampleWriteModel()
{
var averageCsiScoreRange = new List<PointsThresholdWriteModel>
{
new PointsThresholdWriteModel
{
Target = 50,
Points = 1,
},
new PointsThresholdWriteModel
{
Target = 55,
Points = 2,
},
new PointsThresholdWriteModel
{
Target = 60,
Points = 3,
},
};
return new UnitCommissionRulesetWriteModel
{
AverageCsiScoreRange = averageCsiScoreRange,
};
}
}
#region Mapping Logic
public class ExampleProfile : Profile
{
public ExampleProfile()
{
var unitCommissionRulesetMap =
CreateMap<UnitCommissionRulesetWriteModel, UnitCommissionRuleset>();
unitCommissionRulesetMap.MapPerformanceMetricScoreRule(
src => src.AverageCsiScoreRange,
dest => dest.AverageCsiScoreRule,
thresholds => new AverageCsiScoreRule(thresholds));
CreateMap<PointsThresholdWriteModel, UnitCommissionPointsThreshold>();
}
}
internal static class MappingExtensions
{
public static void MapPerformanceMetricScoreRule<TRange, TRule>(
this IMappingExpression<UnitCommissionRulesetWriteModel, UnitCommissionRuleset> map,
Func<UnitCommissionRulesetWriteModel, TRange> rangeSelector,
Expression<Func<UnitCommissionRuleset, TRule>> ruleSelector,
Func<ICollection<UnitCommissionPointsThreshold>, TRule> constructRuleFromThresholds) where TRule : class
{
TRule MapRule(
UnitCommissionRulesetWriteModel src,
UnitCommissionRuleset dest,
TRule rule,
ResolutionContext ctx)
{
var range = rangeSelector(src);
if (range is null)
return null;
var thresholds = ctx.Mapper.Map<List<UnitCommissionPointsThreshold>>(range);
return constructRuleFromThresholds(thresholds);
}
map.ForMember(ruleSelector, opt => opt.MapFrom(MapRule));
}
}
#endregion
#region Write Models
public class UnitCommissionRulesetWriteModel
{
public ICollection<PointsThresholdWriteModel> AverageCsiScoreRange { get; init; }
}
public class PointsThresholdWriteModel
{
public int Target { get; init; }
public int Points { get; init; }
}
#endregion
#region Domain
public class UnitCommissionRuleset
{
public UnitCommissionRuleset(AverageCsiScoreRule? averageCsiScoreRule = null)
{
AverageCsiScoreRule = averageCsiScoreRule;
}
public AverageCsiScoreRule? AverageCsiScoreRule { get; private init; }
}
public class AverageCsiScoreRule
{
public AverageCsiScoreRule(ICollection<UnitCommissionPointsThreshold> thresholds)
{
Thresholds = thresholds.ToList();
}
public IReadOnlyCollection<UnitCommissionPointsThreshold> Thresholds { get; private init; } = null!;
}
public class UnitCommissionPointsThreshold
{
public UnitCommissionPointsThreshold(int target, int points)
{
Target = target;
Points = points;
}
public int Target { get; private init; }
public int Points { get; private init; }
}
#endregion
我已经看到并阅读了MapFrom 在 AutoMapper 11.0.1 中无法正常工作,但是根据该帖子中的讨论和链接文档,我无法取得任何进展。
作为一个实验,我尝试通过将 SSCCE 中的服务设置语句更改为services.AddAutoMapper(cfg => cfg.DisableConstructorMapping(), typeof(ExampleProfile).Assembly);
. 这会导致引发以下异常:
ArgumentException: UnitCommissionPointsThreshold needs to have a constructor with 0 args or only optional args. Validate your configuration for details.
编辑
下面是一个简化的示例,它也演示了不使用 DI 的这个问题:
public class Program
{
public static void Main()
{
var mapperConfiguration = new MapperConfiguration(c => c.AddProfile<ExampleProfile>());
var mapper = mapperConfiguration.CreateMapper();
var writeModel = CreateExampleWriteModel();
var parent = mapper.Map<Parent>(writeModel);
if (parent.ChildGroup is null)
throw new Exception("ChildGroup property was not mapped");
}
private static ParentWriteModel CreateExampleWriteModel()
{
var children = new List<ChildWriteModel>
{
new()
{
Foo = 1
},
new()
{
Foo = 2
},
new()
{
Foo = 3
}
};
return new ParentWriteModel
{
Children = children
};
}
}
#region Mapping Logic
public class ExampleProfile : Profile
{
public ExampleProfile()
{
CreateMap<ParentWriteModel, Parent>().ForMember(
dest => dest.ChildGroup,
opt => opt.MapFrom((src, _, _, ctx) => new ChildGroup(ctx.Mapper.Map<List<Child>>(src.Children))));
CreateMap<ChildWriteModel, Child>();
}
}
#endregion
#region Write Models
public class ParentWriteModel
{
public ICollection<ChildWriteModel> Children { get; init; }
}
public class ChildWriteModel
{
public int Foo { get; init; }
}
#endregion
#region Domain
public class Parent
{
public Parent(ChildGroup? childGroup = null)
{
ChildGroup = childGroup;
}
public ChildGroup? ChildGroup { get; private init; }
}
public class ChildGroup
{
public ChildGroup(ICollection<Child> thresholds)
{
Children = thresholds.ToList();
}
public IReadOnlyCollection<Child> Children { get; } = null!;
}
public class Child
{
public Child(int foo)
{
Foo = foo;
}
public int Foo { get; private init; }
}
#endregion
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.