提问人:Jimmyt1988 提问时间:11/17/2023 最后编辑:Jimmyt1988 更新时间:11/21/2023 访问量:91
如何在 c# AutoMapper 操作中使用服务
How to use services in a c# AutoMapper Action
问:
我设置了一个配置文件:
builder.Services
.AddAutoMapper((IMapperConfigurationExpression a) => {
a.AddProfile<GeneralMappingProfile>();
});
以下是简介:
public class GeneralMappingProfile : Profile
{
public GeneralMappingProfile()
{
CreateMap<Source, Destination>()
.AfterMap<SourceToDestinationAction>()
.ForAllMembers(x => x.Ignore());
}
}
我的操作如下所示:
public class SourceToDestinationAction : IMappingAction<Source, Destination>
{
private readonly IACoolService coolService;
public SourceToDestinationAction(IACoolService _coolService){
coolService = _coolService;
}
public void Process(Source source, Destination destination, ResolutionContext context)
{
destination.Title = coolService.MakeMultiLanguageValue(source.Title);
}
}
我想像这样使用它:
...(IMapper mapper){
...
Destination destinationData = mapper.Map<Destination>(sourceData);
}
这不起作用,因为 Action 需要无参数构造函数。
如何在我的操作中建立依赖关系?
系统信息:
- .净6.0
- 自动映射器 12.0.1
- AutoMapper.Extensions.Microsoft.DependencyInjection
- Web 应用程序
审核注意事项:
- 请注意,这不是配置文件的 DI,而是操作。
上下文信息:
我正在尝试在单元测试中测试映射:
MapperConfiguration config = new (cfg => { GeneralMappingProfile profile = new(); cfg.AddProfile(profile); }); mapper = config.CreateMapper(); mapper.Map(item, content); // Throws error here as SourceToDestinationAction has no paramterless constructor
抛出的错误:
无法动态创建类型为“SourceToDestinationAction”的实例。原因:未定义无参数构造函数。
答:
我不确定您使用的是什么版本,但文档说您确实可以做到这一点。这句话看起来和你想做的一模一样:
Asp.Net Core 和 AutoMapper.Extensions.Microsoft.DependencyInjection
如果您使用的是 Asp.Net Core 和 AutoMapper.Extensions.Microsoft.DependencyInjection 包,这也是使用依赖项注入的好方法。不能将依赖项注入 Profile 类,但可以在 IMappingAction 实现中执行此操作。
以下示例演示如何利用依赖项注入,将访问当前 HttpContext 的 IMappingAction 连接到映射操作后的 Profile:
public class SetTraceIdentifierAction : IMappingAction<SomeModel, SomeOtherModel>
{
private readonly IHttpContextAccessor _httpContextAccessor;
public SetTraceIdentifierAction(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
}
public void Process(SomeModel source, SomeOtherModel destination, ResolutionContext context)
{
destination.TraceIdentifier = _httpContextAccessor.HttpContext.TraceIdentifier;
}
}
public class SomeProfile : Profile
{
public SomeProfile()
{
CreateMap<SomeModel, SomeOtherModel>()
.AfterMap<SetTraceIdentifierAction>();
}
}
评论
问题归结为这样一个事实,即服务集合/服务提供者不知道如何构建实例,无论是否使用 AutoMapper。即使将所有依赖项都添加到服务集合中,它仍然(显然)不知道如何构建对象,如以下代码所示:SourceToDestinationAction
IServiceCollection services = new ServiceCollection();
services.AddSingleton<IACoolService, MyCoolService>();
//services.AddSingleton<SourceToDestinationAction>();
IServiceProvider provider = services.BuildServiceProvider();
provider.GetRequiredService<SourceToDestinationAction>();
这将导致消息“类型'(命名空间)没有服务”。SourceToDestinationAction' 已注册。InvalidOperationException
但是,当您使用 // 之一将类型注册到服务集合时,它可以毫无问题地解析该类型。AddSingleton()
AddScoped()
AddTransient()
IServiceCollection services = new ServiceCollection();
services.AddSingleton<IACoolService, MyCoolService>();
services.AddSingleton<SourceToDestinationAction>();
IServiceProvider provider = services.BuildServiceProvider();
var act = provider.GetRequiredService<SourceToDestinationAction>();
Console.WriteLine(act); // prints "(namespace).SourceToDestinationAction"
之后,AutoMapper 最终也将能够解析该类型。
解决方案是(显式)在服务集合中注册您的类,至少使用 .事实上,当您将程序集实例用作参数时,例如SourceToDestinationAction
AddTransient()
AddAutoMapper()
services.AddAutoMapper(typeof(GeneralMappingProfile).Assembly);
它将自动为您注册所有此类操作,如 ServiceCollectionExtensions.AddAutoMapperClasses 的源代码所示:
var openTypes = new[] { typeof(IValueResolver<,,>), typeof(IMemberValueResolver<,,,>), typeof(ITypeConverter<,>), typeof(IValueConverter<,>), typeof(IMappingAction<,>) }; foreach (var type in assembliesToScan.SelectMany(a => a.GetTypes().Where(type => type.IsClass && !type.IsAbstract && Array.Exists(openTypes, openType => type.GetGenericInterface(openType) != null)))) { // use try add to avoid double-registration services.TryAddTransient(type); }
也许服务集合/服务提供程序可以这样配置,即在未注册类型时自动回退。transient
评论
services.AddSingleton<SourceToDestinationAction>();
SourceToDestinationAction
评论