使用 Dapper 映射嵌套对象列表(3 级嵌套对象)

Posted

技术标签:

【中文标题】使用 Dapper 映射嵌套对象列表(3 级嵌套对象)【英文标题】:Map lists of nested objects with Dapper (3 level nested object) 【发布时间】:2017-08-04 11:02:29 【问题描述】:

我正在使用 Dapper,我有这样的课程:

public class Region

    public Region()
    
        Countries = new List<Country>(0);
    
    public int RegionID  get; set; 

    public int RiskRank  get; set; 

    public string Name  get; set; 

    public int SiteID  get; set; 

    public string DestinationType  get; set; 

    public string HealixCode  get; set; 

    public string AreaCode  get; set; 

    public List<Country> Countries  get; set; 


public class Country

    public Country()
    

    

    public int CountryID  get; set; 

    public bool IsSecondary  get; set; 

    public string Name  get; set; 

    public string ISO2Code  get; set; 

    public string ISO3Code  get; set; 

    public ISOCountry ISOCountry  get; set; 

    public IList<CountryAlias> CountryAliases  get; set; 


public class CountryAlias

    public CountryAlias()
    

    
    public int CountryAliasID  get; set; 
    public int CountryID  get; set; 
    public string Alias  get; set; 

我可以获取有关所有国家/地区的所有地区信息,但我想知道是否可以通过一个查询获取每个国家/地区的 CountryAlias 列表。其实我做的是这样的:

private const string GetAllForSiteWithoutCountriesSQL = @"SELECT * FROM Regions WHERE ChannelID = @channelID";  

private const string GetAllForSiteWithCountriesSQL = @"SELECT c.*, rc.RegionID
                                                           FROM Regions r
                                                             JOIN RegionCountries rc ON rc.RegionID = r.RegionID
                                                             JOIN Countries c ON (rc.CountryID = c.CountryID AND c.IsSecondary = 0)                                                                 
                                                           WHERE r.ChannelID = @channelID";

    public async Task<IEnumerable<Region>> GetAllAsync(int channelID, bool includeCountries = true)
    
        var regions = await Database.QueryAsync<Region>(GetAllForSiteWithoutCountriesSQL, new  channelID );

        var regionMap = regions.ToDictionary(r => r.RegionID);

        if (includeCountries)
        
            await Database.QueryAsync<Country, int, Country>(
                GetAllForSiteWithCountriesSQL,
                (country, regionID) =>
                
                    regionMap[regionID].Countries.Add(country);
                    return country;
                , new  channelID , splitOn: "RegionID");
        

        return regions;
    

我还找到了一个很好的解释here,但我不明白如何在我的情况下使用它,因为我也有 Group 类。我应该如何使用 Dapper 执行此操作,是否有可能或唯一的方法是执行不同的步骤?谢谢

【问题讨论】:

有几个选项,看看这个thread @franc 他已经提到他在他的问题中调查了该线程。 我建议看看this,我自己试过了,效果不错。 【参考方案1】:

我知道这个问题很老了,但我前段时间也遇到过同样的问题,对于那些仍有疑问的人,你可以尝试以下。

两个查询的 SQL:

SELECT r.*, c.*, a.*
    FROM Regions as r
        LEFT JOIN Countries as c 
            ON a.RegionID = c.RegionID
                LEFT JOIN CountryAliases as a
                    ON a.RegionID = a.RegionID
    WHERE r.ChannelID  = @ChannelID 

您的 Region 对象仍然没有 ChannelID 属性,因此您可以这样做:

C#

New RegionID = channelID

VB.NET

New With .RegionID = channelID

我也不知道这个频道是关于什么的,但似乎可以作为您搜索的关键。

C#

Dictionary<int, Region> RegionDictionary = new Dictionary<int, Region>();
Await Database.QueryAsync<Region, Country, CountryAlias, Region>(sql,
    (region, country, countryalias) => 
    Region _region = new Region();

    if(!RegionDictionary.TryGetValue(region.RegionID, out _region))
        RegionDictionary.Add(region.RegionID, _region = region);
    

    if(_region.Countries == null)
        _region.Countries = new List<Country>();
    
    if(countryalias != null)
        // begin <this line might be discarded>
        if(country.CountryAliases == null)
            country.CountryAliases = new List<CountryAlias>();
        
        // end
        country.CountryAliases.Add(countryalias);
    
    _region.Countries.Add(country);
    return _region;
, new channelID, splitOn: "CountryID, CountryAliasID, RegionID");

return RegionDictionary.Values.ToList();

VB.NET

Dim RegionDictionary As New Dictionary(Of Integer, Region)
Await Database.QueryAsync(Of Region, Country, CountryAlias, Region)(sql,
Function(region, country, countryalias)
    Dim _region As New Region();
    if(!RegionDictionary.TryGetValue(region.RegionID, _region)) Then
        _region = region
        RegionDictionary.Add(region.RegionID, region)
        If IsNothing(_region.Countries) Then
            _region.Countries = new List(Of Country)
        End If
        If Not IsNothing(countryalias) Then
            ' begin <this line might be discarded>
            If IsNothing(country.CountryAliases) Then
                country.CountryAliases = new List(Of CountryAlias)
            End If
            ' end
            country.CountryAliases.Add(countryalias)
        End If
        _region.Countries.Add(country)
    End If
End Function, New With channelID, splitOn: "CountryID, CountryAliasID, RegionID")

Return RegionDictionary.Values.ToList()

如果您想知道如何使用相同的逻辑 work more than 7 types,您可以这样做:

C#

Dictionary<int, Region> RegionDictionary = new Dictionary<int, Region>();
Await Database.QueryAsync<Region>(sql,
    new[]
    
        typeof(Region),
        typeof(Country),
        typeof(CountryAlias)
    ,
    obj => 
        Region region = obj[0] as Region;
        Country country = obj[1] as Country;
        CountryAlias countryalias = obj[2] as CountryAlias;
        Region _region = new Region();

        if(!RegionDictionary.TryGetValue(region.RegionID, out _region))
            RegionDictionary.Add(region.RegionID, _region = region);
        

        if(_region.Countries == null)
            _region.Countries = new List<Country>();
        
        if(countryalias != null)
            // begin <this line might be discarded>
            if(country.CountryAliases == null)
                country.CountryAliases = new List<CountryAlias>();
            
            // end
            country.CountryAliases.Add(countryalias);
        
        _region.Countries.Add(country);
        return _region;
, new channelID, splitOn: "CountryID, CountryAliasID, RegionID");

return RegionDictionary.Values.ToList();

这一切都是关于创建一个字典,然后将一些值放入其中,这取决于是否已经存在具有相应 ID 的值。

【讨论】:

以上是关于使用 Dapper 映射嵌套对象列表(3 级嵌套对象)的主要内容,如果未能解决你的问题,请参考以下文章

嵌套多映射 Dapper 分页查询中的重复字段名称问题

如何使用 AutoMapper 使用 EntityFramework 使用嵌套列表更新对象?

Automapper 链/嵌套对象映射抛出异常

如何使用从 Flutter 中的 json 解析的嵌套映射列表中的函数创建对象

rails 在哪里获取嵌套对象的查询?

C# automapper 嵌套对象条件映射