天天看點

說說ABP項目中的AutoMapper,Castle Windsor(痛并快樂着)

這篇部落格要說的東西跟ABP,AutoMapper和Castle Windsor都有關系,而且也是我在項目中遇到的問題,最終解決了,現在的感受就是“痛并快樂着”。

首先,這篇部落格不是講什麼新的知識點,而是一次實戰項目的經驗總結,其實更是一次彎路或者錯誤記錄吧,友善現在或以後遇到同樣問題的人。

下面開始總結。

先來說說我的功能需求:

我要在頁面上顯示裝置所在的城市名稱,但是裝置實體類中對應的字段是CityId,也就是城市表所對應的Id主鍵,但是裝置實體類所對應的Dto,映射鍊所對應的屬性是City。這裡就不貼代碼了,你隻要記住我要從裝置實體類的CityId屬性映射到裝置Dto類的City屬性,這個映射是通過AutoMapper來完成,而且CityId是Int32類型,而City是string類型,是以,映射的過程中肯定是要通過CityId查詢出City的名稱進而映射到目标屬性。通過一張圖來表達就是這樣的效果:

說說ABP項目中的AutoMapper,Castle Windsor(痛并快樂着)

這整個過程都還好了解,問題出在具體的實作代碼上了。

因為之前寫過AutoMapper的系列教程(如果您還不知道什麼是AutoMapper,請點選這裡),是以我想到了使用自定義值解析器,是以自己寫了個類:

/// <summary>
/// 
/// 自定義值解析器,目的在于将TerminalDevices的CityId映射成具體的string類型的城市名稱
/// </summary>
public class TerminalDeviceResolver : ValueResolver<TerminalDevices, string>
{
     private  readonly ICityAppService _cityAppService ;
    
     public TerminalDeviceResolver(ICityAppService cityAppService)
    {
        _cityAppService = cityAppService;
    }

    protected override string ResolveCore(TerminalDevices source)
    {
        if (source.CityID.HasValue)
        {
            var cityId = Convert.ToInt32(source.CityID);
            var city=_cityAppService.GetCity(new CityInput() { Id = cityId });
            if (city!=null)
            {
                return city.Name;
            }
            return "找不到該城市";
        }
        return "CityID為空";
    }
}      

然後再在配置類中修改一下配置:

Mapper.CreateMap<TerminalDevices, TerminalDeviceOutput>().ForMember(output => output.City, opt =>
{
    opt.ResolveUsing<TerminalDeviceResolver>();
});      

原本以為這樣就沒問題了,結果問題來了:

說說ABP項目中的AutoMapper,Castle Windsor(痛并快樂着)

這裡報錯說“類型需要有一個具有0個參數或者隻有可選參數的構造函數”。我猜想肯定是說自定義解析器類,于是又定義了個無參的構造函數。

public TerminalDeviceResolver() { }      

問題又來了:

說說ABP項目中的AutoMapper,Castle Windsor(痛并快樂着)

經過調試,我發現這裡的_cityAppService為null,那我就納悶了,我上面不是已經通過構造函數注入了該服務接口對象了嘛,怎麼會是null呢?仔細調試發現,程式走的是無參的構造函數,沒有走這個依賴注入的構造函數,怪不得為null呢?

找到原因之後,我就又思考了,如何通過構造函數注入依賴呢?我又想到了AutoMapper中自定義解析器的ConstructedBy方法,接下來我又這樣修改了一下映射配置類:

public class TerminalDeviceProfile:Profile
{
    private  readonly ICityAppService _cityAppService ;
    public TerminalDeviceProfile(ICityAppService cityAppService)
    {
        _cityAppService = cityAppService;
    }
    protected override void Configure()
    {
        Mapper.CreateMap<TerminalDevices, TerminalDeviceDto>();
        Mapper.CreateMap<TerminalDevices, TerminalDeviceOutput>().ForMember(output => output.City, opt =>
        {
             opt.ResolveUsing<TerminalDeviceResolver>().ConstructedBy(() => new TerminalDeviceResolver(_cityAppService));
        });
    }
 }      

問題再次來了,真的是一環套一環啊,這次是編譯錯誤:

說說ABP項目中的AutoMapper,Castle Windsor(痛并快樂着)

可見,不能再配置類中通過構造函數進行依賴注入,突然又想到ABP中可以這樣做,直接解析:

var cityAppService = IocManager.Instance.Resolve<ICityAppService>();      

是以,最終代碼是這樣的:

public class TerminalDeviceProfile : Profile
{

    protected override void Configure()
    {
        var cityAppService = IocManager.Instance.Resolve<ICityAppService>();
        Mapper.CreateMap<TerminalDevices, TerminalDeviceDto>();
        Mapper.CreateMap<TerminalDevices, TerminalDeviceOutput>()
            .ForMember(output => output.City, opt =>
            {
                opt.ResolveUsing<TerminalDeviceResolver>()
                    .ConstructedBy(() => new TerminalDeviceResolver(cityAppService));
            });
    }
}      

最終效果如下:

說說ABP項目中的AutoMapper,Castle Windsor(痛并快樂着)

順便看一下響應封包:

說說ABP項目中的AutoMapper,Castle Windsor(痛并快樂着)

總之,折騰了這麼久(痛苦),沒白忙活,總算問題解決了,我的心情此時是快樂的!

如果您認為這篇文章還不錯或者有所收獲,您可以通過右邊的“打賞”功能 打賞我一杯咖啡【物質支援】,也可以點選右下角的【好文要頂】按鈕【精神支援】,因為這兩種支援都是我繼續寫作,分享的最大動力!

作者:tkb至簡

來源:http://farb.cnblogs.com/

聲明:原創部落格請在轉載時保留原文連結或者在文章開頭加上本人部落格位址,如發現錯誤,歡迎批評指正。凡是轉載于本人的文章,不能設定打賞功能,如有特殊需求請與本人聯系!

已将所有贊助者統一放到單獨頁面!簽名處隻保留最近10條贊助記錄!檢視贊助者清單

衷心感謝打賞者的厚愛與支援!也感謝點贊和評論的園友的支援!
打賞者 打賞金額 打賞日期
微信:匿名 10.00 2017-08-03
2017-08-04
5.00 2017-06-15
支付寶:一個名字499***@qq.com 2017-06-14
16.00 2017-04-08
支付寶:向京劉 2017-04-13
2017-003-08
2017-03-08
支付寶:lll20001155 2017-03-03
支付寶:她是一個弱女子 2017-03-02

繼續閱讀