如何使用 Mapster 映射自引用?

How to map self-reference with Mapster?

我有一个列表,其中包含自引用的项目。 例如:
型号

public class Stock{
  public int StockId { get; set; }
  public int? ParentStockId { get; set; }
  public int Level { get; set; }
  public string Name { get; set; }
}

视图模型

public class StockViewModel {
  public int Level { get; set; }
  public string Name { get; set; }
  public IEnumerable<StockViewModel> SubStock { get; set; }
}

示例数据

var stocks = new List<Stock> {
    new Stock{ StockId = 1, Level = 10, Name = "Root1" },
    new Stock{ StockId = 2, Level = 10, Name = "Root2" },
    new Stock{ StockId = 3, ParentStockId = 1, Level = 20, Name = "Area1" },
    new Stock{ StockId = 4, ParentStockId = 2, Level = 20, Name = "Area2" },
    new Stock{ StockId = 5, ParentStockId = 3, Level = 30, Name = "Box11" },
    new Stock{ StockId = 6, ParentStockId = 3, Level = 30, Name = "Box12" },
    new Stock{ StockId = 7, ParentStockId = 4, Level = 30, Name = "Box21" }
};

我使用类似的代码来达到预期的效果。但我确定这是错误的。我必须在每个新请求之前进行配置。

        var mapper = new Mapper(); 
        TypeAdapterConfig<Stock, StockViewModel>
            .NewConfig()
            .Map(dst => dst.SubStock, src => stocks.Where(x => x.ParentStockId == src.StockId).ToList());
        var viewStocks = mapper.Map<IEnumerable<Stock>, IEnumerable<StockViewModel>>(stocks.Where(x => x.ParentStockId == null));
        // Sample output
        foreach (var stock in viewStocks){
            Console.WriteLine(stock.Level.ToString() + " " + stock.Name);
            if (stock.SubStock != null) 
                foreach (var stock1 in stock.SubStock){
                    Console.WriteLine("  " + stock1.Level.ToString() + " " + stock1.Name);
                    if (stock1.SubStock != null) 
                        foreach (var stock2 in stock1.SubStock){
                            Console.WriteLine("    " + stock2.Level.ToString() + " " + stock2.Name);
                        }
                }
        }

Fiddle
如何映射viewStocks层级? 与

相同
10 Root1
  20 Area1
    30 Box11
    30 Box12
10 Root2
  20 Area2
    30 Box21

您不需要为每个新请求配置 Mapster 配置。根据 https://github.com/MapsterMapper/Mapster/wiki/Config-location:

Configuration should be set only once and reuse for mapping. Therefore, we should not keep configuration and mapping in the same location.Configuration should keep in entry point such as Main function or Global.asax.cs or Startup.cs.

这样您就可以将该映射移动到入口点:

TypeAdapterConfig<Stock, StockViewModel>
    .NewConfig()
    .Map(dst => dst.SubStock, src => stocks.Where(x => x.ParentStockId == src.StockId).ToList());

此外,为了分离您的配置和映射实例而不是这个

 var viewStocks = mapper.Map<IEnumerable<Stock>, IEnumerable<StockViewModel>>(stocks.Where(x => x.ParentStockId == null));

我更喜欢使用 Adapt 如下:

var socksWithoutParent = stocks.Where(x => x.ParentStockId == null);
var viewStocks = socksWithoutParent.Adapt<List<StockViewModel>>();

完整示例Fiddle,它会给你相同的输出:

10 Root1
  20 Area1
    30 Box11
    30 Box12
10 Root2
  20 Area2
    30 Box21

为了正确分离配置和映射,我传递了一个带有 stocks

的参数
var viewStocks = stocks.Where(x => x.ParentStockId == null)
            .BuildAdapter()
            .AddParameters("paramKey", stocks)
            .AdaptToType<List<StockViewModel>>();

和我的配置

TypeAdapterConfig<Stock, StockViewModel>
            .NewConfig()
            .Map(dst => dst.SubStock, 
                 src => ((IEnumerable<Stock>)MapContext.Current.Parameters["paramKey"])
                 .Where(x => x.ParentStockId == src.StockId)
                 .BuildAdapter()
                 .AddParameters("paramKey", MapContext.Current.Parameters["paramKey"])
                 .AdaptToType<List<StockViewModel>>());

full example

现在在我的配置中我不需要知道 stocks