我想创建.Net Core类库,其中将包含以下扩展方法:
public static class MyServiceExtensions
{
public static IServiceCollection AddMyService<TUserDto, TUserDtoKey, TUser, TUserKey>(this IServiceCollection services)
where TUserDto : UserDto<TUserDtoKey>
where TUser : User<TUserKey>
{
services.AddAutoMapper(config =>
{
config.AddProfile<UserMappingProfile<TUserDto, TUserDtoKey, TUser, TUserKey>>();
});
return services;
}
}
我有以下自动映射器配置文件:
public class UserMappingProfile<TUserDto, TUserDtoKey, TUser, TUserKey> : Profile
where TUserDto : UserDto<TUserDtoKey>
where TUser : User<TUserKey>
{
public UserMappingProfile()
{
CreateMap<TUserDto, TUser>(MemberList.Destination)
.ForMember(x => x.Id, opts => opts.MapFrom(x => x.UserId));
CreateMap<TUser, TUserDto > (MemberList.Source)
.ForMember(x => x.UserId, opts => opts.MapFrom(x => x.Id));
}
}
这些实体:
public class UserDto<TKey>
{
public TKey UserId { get; set; }
public string UserName { get; set; }
}
public class User<TKey>
{
public TKey Id { get; set; }
public string UserName { get; set; }
}
public class MyUser : User<int>
{
public string Email { get; set; }
}
public class MyUserDto : UserDto<int>
{
public string Email { get; set; }
}
如果我尝试这样使用它:
services.AddMyService<MyUserDto, int, MyUser, int>();
我收到此错误:
{System.ArgumentException:无法创建的实例 GenericMapping.Services.Mapping.UserMappingProfile
4[TUserDto,TUserDtoKey,TUser,TUserKey] because Type.ContainsGenericParameters is true. at System.RuntimeType.CreateInstanceCheckThis() at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean wrapExceptions, Boolean skipCheckThis, Boolean fillCache) at System.Activator.CreateInstance(Type type, Boolean nonPublic, Boolean wrapExceptions) at AutoMapper.Configuration.MapperConfigurationExpression.AddProfile(Type profileType) in C:\projects\automapper\src\AutoMapper\Configuration\MapperConfigurationExpression.cs:line 44 at AutoMapper.ServiceCollectionExtensions.<>c__DisplayClass10_0.<AddAutoMapperClasses>g__ConfigAction|4(IMapperConfigurationExpression cfg) in C:\projects\automapper-extensions-microsoft-dependencyinjectio\src\AutoMapper.Extensions.Microsoft.DependencyInjection\ServiceCollectionExtensions.cs:line 83 at AutoMapper.MapperConfiguration.Build(Action
1配置)中 C:\ projects \ automapper \ src \ AutoMapper \ MapperConfiguration.cs:第307行 在 AutoMapper.ServiceCollectionExtensions.AddAutoMapperClasses(IServiceCollection 服务,动作1 additionalInitAction, IEnumerable
1 AssemblyToScan)中 C:\ projects \ automapper-extensions-microsoft-dependencyinjectio \ src \ AutoMapper.Extensions.Microsoft.DependencyInjection \ ServiceCollectionExtensions.cs:line 89分 GenericMapping.Services.Extensions.MyServiceExtensions.AddMyService [TUserDto,TUserDtoKey,TUser,TUserKey](IServiceCollection 服务)中 C:\ Projects \ GenericMapping \ GenericMapping.Services \ Extensions \ MyServiceExtensions.cs:line 14在GenericMapping.Startup.ConfigureServices(IServiceCollection 服务)在C:\ Projects \ GenericMapping \ GenericMapping \ Startup.cs:line中 33}
如何解决此问题?
答案 0 :(得分:3)
问题的根本原因是 AddAutoMapper 扩展方法的使用不正确。此方法扫描程序集的配置文件(和其他AutoMapper组件),并使用找到的配置在DI容器中注册 IMapper 组件。 (我建议您看看its sources,以了解幕后到底发生了什么。)
之所以会出现异常,是因为 AddAutoMapper 找到了 UserMappingProfile 类,但是由于有4个开放类型参数,因此不知道如何实例化它。
解决问题的最简单方法是使通用概要文件类抽象化,并使用所需的类型参数对其进行子类化:
public abstract class UserMappingProfile<TUserDto, TUserDtoKey, TUser, TUserKey> : Profile
where TUserDto : UserDto<TUserDtoKey>
where TUser : User<TUserKey>
{
public UserMappingProfile()
{
CreateMap<TUserDto, TUser>(MemberList.Destination)
.ForMember(x => x.Id, opts => opts.MapFrom(x => x.UserId));
CreateMap<TUser, TUserDto>(MemberList.Source)
.ForMember(x => x.UserId, opts => opts.MapFrom(x => x.Id));
}
}
public class UserMappingProfile : UserMappingProfile<MyUserDto, int, MyUser, int> { }
现在您根本不需要 MyServiceExtensions ,只需一个services.AddAutoMapper()
调用,您的配置就会自动被提取。
但是,如果您坚持使用自己的扩展方法进行配置,则必须避免 AddAutoMapper ,因为它只能被调用一次。您可以提供自己的注册逻辑,而不必扫描 Profile 类的程序集。使用构建器模式的示例:
public class UserMappingProfile<TUserDto, TUserDtoKey, TUser, TUserKey> : Profile
where TUserDto : UserDto<TUserDtoKey>
where TUser : User<TUserKey>
{
public UserMappingProfile()
{
CreateMap<TUserDto, TUser>(MemberList.Destination)
.ForMember(x => x.Id, opts => opts.MapFrom(x => x.UserId));
CreateMap<TUser, TUserDto>(MemberList.Source)
.ForMember(x => x.UserId, opts => opts.MapFrom(x => x.Id));
}
}
public interface IMapperConfigurationBuilder
{
IMapperConfigurationBuilder UseProfile<TUserDto, TUserDtoKey, TUser, TUserKey>()
where TUserDto : UserDto<TUserDtoKey>
where TUser : User<TUserKey>;
}
public static class MyServiceExtensions
{
private class MapperConfigurationBuilder : IMapperConfigurationBuilder
{
public HashSet<Type> ProfileTypes { get; } = new HashSet<Type>();
public IMapperConfigurationBuilder UseProfile<TUserDto, TUserDtoKey, TUser, TUserKey>()
where TUserDto : UserDto<TUserDtoKey>
where TUser : User<TUserKey>
{
ProfileTypes.Add(typeof(UserMappingProfile<TUserDto, TUserDtoKey, TUser, TUserKey>));
return this;
}
}
public static IMapperConfigurationBuilder AddMyMapper(this IServiceCollection services)
{
var builder = new MapperConfigurationBuilder();
services.AddSingleton<IConfigurationProvider>(sp => new MapperConfiguration(cfg =>
{
foreach (var profileType in builder.ProfileTypes)
cfg.AddProfile(profileType);
}));
services.AddScoped<IMapper>(sp => new Mapper(sp.GetRequiredService<IConfigurationProvider>(), sp.GetService));
return builder;
}
}
然后,映射配置文件注册将如下所示:
services.AddMyMapper()
.UseProfile<MyUserDto, int, MyUser, int>();