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


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

首先,這篇博客不是講什么新的知識點,而是一次實戰項目的經驗總結,其實更是一次彎路或者錯誤記錄吧,方便現在或以后遇到同樣問題的人。

下面開始總結。

先來說說我的功能需求:

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

image

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

因為之前寫過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>();
});

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

image

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

public TerminalDeviceResolver() { }

問題又來了:

image

經過調試,我發現這里的_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));
        });
    }
 }

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

image

可見,不能再配置類中通過構造函數進行依賴注入,突然又想到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));
            });
    }
}

最終效果如下:

image

順便看一下響應報文:

image

 

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM