代码之家  ›  专栏  ›  技术社区  ›  Jenan

如何将泛型配置文件与Automapper和Asp.Net核心依赖注入一起使用

  •  2
  • Jenan  · 技术社区  · 6 年前

    我要创建.Net核心类库,该类库将包含以下扩展方法:

    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>();
    

    我得到这个错误:

    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 C: \projects\automapper\src\automapper\mappenconfiguration.cs:第307行 AutoMapper.ServiceCollectionExtensions.AddAutoMapperClasses(IServiceCollection 1 additionalInitAction, IEnumerable 1个 C: \projects\automapper extensions microsoft dependencyinjectio\src\automapper.extensions.microsoft.DependencyInjection\ServiceCollectionExtensions.cs:line 89在 服务)中 C: \Projects\GenericMapping\GenericMapping.Services\Extensions\myserviceeextensions.cs:line 服务)在C:\项目\常规应用程序\常规应用程序\启动.cs:line中 33}

    我怎样才能解决这个问题?

    1 回复  |  直到 6 年前
        1
  •  3
  •   Adam Simon    6 年前

    问题的根本原因是 扩展方法。此方法扫描程序集以查找配置文件(和其他自动应用程序组件)并注册 图像 使用找到的配置的DI容器中的组件。(我建议你看看 its sources

    你得到例外是因为 添加自动应用程序 用户映射配置文件 类,但不知道如何实例化它,因为它有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> { }
    

    现在你不需要 MyService扩展 services.AddAutoMapper() 呼叫后,您的配置将自动获取。

    添加自动应用程序 因为它只被调用一次。而不是扫描程序集 类,您可以提供自己的注册逻辑。使用生成器模式的示例:

    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>();