簡體   English   中英

用於 AutoMapper 的 Asp Net Core DI

[英]Asp Net Core DI for AutoMapper

我有一個關於 ASP NET Core 中 AutoMapper 依賴注入的問題。 我知道在實現自定義IValueResolverIMemberValueResolver時,DI 有 automapper 擴展可用。 這僅在 AutoMapper/DI 創建自定義值解析器時才有效。 不幸的是,如果我需要手動創建值解析器,這將不起作用。 另請注意,當我調用mapper.Map<>()方法時,我不想傳遞項目,因為我不希望IMapper的使用者在運行時意識到任何額外的參數

考慮下面的代碼:

class Entity1
{
    public int MyProperty1 { get; set; } 
}

class Dto1 
{
    public int MyProperty1 { get; set; } 
    public string CustomProperty { get; set; } 
}

在 MyProfile.cs 中

CreateMap<Entity1, Dto1>()
    .ForMember(x => x.CustomProperty, 
       opt => opt.ResolveUsing(new MyCustomPropertyResolver("Important value")));

而 MyCustomPropertyResolver 是這樣的:

public class MyCustomPropertyResolver : IValueResolver<Entity1, Dto1, string>
{
    string _someValue;
    public MyCustomPropertyResolver(string someValue)
    {
        _someValue = someValue;
    }
    string Resolve(Entity1 source, Dto1 destination, string destMember, ResolutionContext context)
    {
        //I need IHttpContextAccessor ..... How can I do that???
    } 
}

當然,如果我這樣做:

CreateMap<Entity1, Dto1>()
    .ForMember(x => x.CustomProperty, 
       opt => opt.ResolveUsing<MyCustomPropertyResolver>()));

我可以使用 DI 並將IHttpContextAccessor添加到MyCustomPropertyResolver構造函數,但是我將無法將任何額外的參數傳遞給解析器,這些參數對於解析實際值也很重要。 有沒有辦法實現這一目標? 我能夠實現這一點的唯一方法是在MyProfile類上添加一個靜態屬性,並通過服務請求的控制器上的ActionFilter設置它。 雖然這項工作但我不喜歡這個解決方案,因為它會產生不需要的依賴項。 AutoMapper 上的官方解決方案是這樣做的:

// This solution is not good enough for my need
var dto = mapper.Map<Dto1>(entity, opt => { opt.Items["AnyThing"] = Whatever; }); 

使用上面的代碼,我可以將HttpContext傳遞給 items 字典,但這將使映射器使用者需要傳入可能不知道的參數。

我的代碼實際上要復雜得多,但它在概念上使用了上面相同的示例。

AutoMapper.Extensions.Microsoft.DependencyInjection將自動注冊從IValueResolver (和一些其他接口)繼承的任何具有瞬態作用域的服務。 出於這個原因,您需要確保您的自定義解析器具有默認構造函數(否則,如果您唯一的構造函數采用字符串參數,則在 AutoMapper 嘗試自動注冊您的自定義解析器時會出錯)。

public MyCustomPropertyResolver()
{
}

然后,您可以使用調用非默認構造函數的實際實例(使用字符串參數)創建映射:

CreateMap<Source, Destination>()
.ForMember(x => x.CustomProperty, 
   opt => opt.MapFrom(new MyCustomPropertyResolver("Important value")));

不幸的是,由於您沒有使用 DI 來實例化自定義解析器,DI 將無法將其他服務直接注入自定義解析器。 解決方法是將這些服務注入解析器的使用者,例如控制器,並通過 Items 集合將它們傳遞到解析器:

_mapper.Map<Destination>(source, opts => { opts.Items["MyService"] = _myServiceInstance; });

編輯 1:

對於更優雅的解決方案,您的自定義解析器可以利用完全依賴注入並在構造函數中采用字符串參數,請考慮以下事項:

您的自定義解析器可能如下所示:

public class MyCustomPropertyResolver : IValueResolver<Source, Destination, string>
{
    private readonly MyService _myService;
    // plus others you need/have registered

    // still need at least one constructor without the string!
    public MyCustomPropertyResolver(MyService _myService)
    {
        _myService = myService;
    }

    public MyCustomPropertyResolver(MyService myService, string fieldName)
    {
        _myService = myService;

        // do something important with fieldName
    }

    // rest excluded
}

為您的自定義解析器創建一個工廠類:

public class MyCustomPropertyResolverFactory
{
    static internal IServiceProvider _provider;
    public static void Configure(IServiceProvider serviceProvider)
    {
        _provider = serviceProvider;
    }

    public static MyCustomPropertyResolver CreateResolverFor(string importantValue)
    {
        return ActivatorUtilities.CreateInstance<MyCustomPropertyResolver>(_provider, importantValue);
    }
}

在 Startup.cs 中配置

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    MyCustomPropertyResolverFactory.Configure(app.ApplicationServices);

    // rest excluded
}

在您的地圖中使用它

CreateMap<Source, Destination>()
.ForMember(x => x.CustomProperty, 
   opt => opt.MapFrom(MyCustomPropertyResolverFactory.CreateResolverFor("Important value")));

當然,您可以使工廠完全通用:

public class MyFactory
{
    static internal IServiceProvider _provider;
    public static void Configure(IServiceProvider serviceProvider)
    {
        _provider = serviceProvider;
    }

    public static T Create<T>(params object[] parameters)
    {
        return ActivatorUtilities.CreateInstance<T>(_provider, parameters);
    }
}

並使用:

.ForMember(d => d.MyProperty, opt => opt.MapFrom(MyFactory.Create<MyResolver>("some string or any number of other parameters")))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM