【问题标题】:How to use generic Profile with Automapper and Asp.Net Core Dependency Injection如何将通用配置文件与 Automapper 和 Asp.Net 核心依赖注入一起使用
【发布时间】:2018-08-20 09:17:52
【问题描述】:

我想创建包含以下扩展方法的 .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] 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(Action1 配置)中 C:\projects\automapper\src\AutoMapper\MapperConfiguration.cs:307 行 在 AutoMapper.ServiceCollectionExtensions.AddAutoMapperClasses(IServiceCollection 服务,行动1 additionalInitAction, IEnumerable1 组件ToScan)在 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}

我该如何解决这个问题?

【问题讨论】:

  • AM 支持 SourceLink,因此应该很容易通过代码来了解发生了什么。

标签: c# asp.net-core .net-core automapper


【解决方案1】:

问题的根本原因是 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>();

【讨论】:

    猜你喜欢
    • 2018-01-03
    • 1970-01-01
    • 2019-06-07
    • 2018-03-25
    • 1970-01-01
    • 2018-02-22
    • 1970-01-01
    • 1970-01-01
    • 2021-02-09
    相关资源
    最近更新 更多