首页 文章

如何使用通用配置文件与Automapper和Asp.Net核心依赖注入

提问于
浏览
2

我想创建.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;
        }
    }

我有以下Automapper配置文件:

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.UserMappingProfile4 [TUserDto,TUserDtoKey,TUser,TUserKey]的实例,因为Type.ContainsGenericParameters为true . 在System.RuntimeType.CreateInstanceCheckThis()处于System.RuntimeType.CreateInstanceSlow(Boolean publicOnly,Boolean wrapExceptions,Boolean skipCheckThis,Boolean fillCache),位于AutoMapper.Configuration.MapperConfigurationExpression的System.Activator.CreateInstance(Type type,Boolean nonPublic,Boolean wrapExceptions) . AddProfile(Type profileType)位于C:\ projects \ _自动化器\ src \ AutoMapper \ Configuration \ MapperConfigurationExpression.cs:第44行的AutoMapper.ServiceCollectionExtensions . <> c__DisplayClass10_0 . <AddAutoMapperClasses> g__ConfigAction | 4(IMapperConfigurationExpression cfg)在C:\ projects \ automapper-extensions-microsoft-dependencyinjectio \ src \ AutoMapper.Extensions.Microsoft.DependencyInjection \ ServiceCollectionExtensions.cs:位于C:\ projects \ automapper \ src \ AutoMapper \ MapperConfiguration.cs中AutoMapper.MapperConfiguration.Build(Action1 configure)的第83行: AutoMapper.ServiceCollectionExtensions.AddAutoMapperClasses的第307行(IServiceCollection服务,Action1 additionalInitAction) ,IEnumerable1 assembliesToScan)在C:\ projects \ automapper-extensions-microsoft-dependencyinjectio \ src \ AutoMapper.Extensions.Microsoft.DependencyInjection \ ServiceCollectionExtensions.cs:第89行,GenericMapping.Services.Extensions.MyServiceExtensions.AddMyService [TUserDto,TUserDtoKey,TUser ,TUserKey](IServiceCollection服务),位于C:\ Projects \ GenericMapping,GenericMapping \ Startup.cs中的C: :第33行

我该如何解决这个问题?

1 回答

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

相关问题