Linq:使用左连接将具有泛型类型的列表组合成泛型类型

Linq : Combine a list with generic type using left join into a generic type

我创建了一个通用函数来构建平面列表,它需要两个参数,其中一个是项目列表,另一个是 return 类型的 DTO,我已经进行了左连接将项目列表与其他列表组合并获得它们的结果

public async Task<List<TContract>> GetFlatList<TInput, TContract>(List<TInput> items)
              where TContract : IOther<TContract>, new() // inherit from IItem
              where TInput : IItem
        {
             var itemIds = items.Select(s => s.ItemId).ToList();
             var otherList= await otherService.GetOthers(itemIds).ConfigureAwait(false);

             var data = otherList
                .GroupJoin(
                    items,
                    other=> other.ItemId,
                    items => items.ItemId,
                    (other, items) => new { other, items })
               .SelectMany(
                 x => x.items.DefaultIfEmpty(),
                 (other, items) => new TContract(){
                      ItemId = other.ItemId,
                      ItemName = items.ItemName,
                      // where items has other prop and it doesn't typed it returns null 
                  }).ToList();
             return data;
        
}

public interface IItem
{
   int ItemId { get; set; }
}
public interface IOther<TContract> : IItem
    {
        string ItemName { get; set; }
    }

在那种情况下,它可以工作,但我不知道项目列表中出现的类型,只是我知道它们共享 ItemId ,而 DTO returns 中的其他字段与 null , 我试过 return 它作为匿名类型但在那种情况下加入不起作用, 所以我需要 return DTO 中的新连接数据,无论其中的任何字段,而不使用匿名类型。

注: 全部通过 arguments 也继承自 IItem & IOther

    public class Item : IItem
    {
        public int ItemId { get; set; }

        public DateTime CreatedDate { get; set; }
    }
   public class OtherList
    {
        public int ItemId { get; set; }

        public string UserName { get; set; }
  
        public string ItemName{ get; set; }

        public DateTime DateOfBirth { get; set; }
    } 

我需要的最终结果是:

public class OtherContract: IOther<OtherContract>
    {
        public int ItemId { get; set; }

        public string ItemName { get; set; }
    
        public string UserName { get; set; }

        public DateTime DateOfBirth { get; set; }

        public DateTime CreatedDate { get; set; }
    }

用法 var data = await GetFlatList<Item,OtherContract>(itemList);

@MikeHofer 发布的这个 fiddle 是迄今为止最好的解决方案,希望这对其他人有所帮助。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

public class Program
{
   public static void Main()
   {
      var items = new List<Item> {
         new Item { ItemId = 2 },
         new Item { ItemId = 3 }
      };

      var result = GetFlatList<Item, OtherContract>(items);
   }

   public static Task<List<TContract>> GetFlatList<TInput, TContract>(List<TInput> items)
              where TContract : IOther<TContract>, new() // inherit from IItem
              where TInput : IItem
   {
      var itemIds = items.Select(s => s.ItemId).ToList();
      var otherList = new OtherService().GetOthers(itemIds);

      var data = otherList.GroupJoin(
                  items,
                  o => o.ItemId, // otherList
                  i => i.ItemId, // items
                  (thisOther, thisItem) => new IntermediateResult
                  {
                     Other = thisOther,
                     Item = (IEnumerable<IItem>)thisItem
                  })
            .Where(i => i.Item.Any())
            .ToList();

      var list = data.SelectMany(
                        x => x.Item.DefaultIfEmpty(),
                        (x, item) => GetContract(x)).ToList();

      return (Task<List<TContract>>)(object)list;
}

   public static OtherContract GetContract(IntermediateResult intermediateResult)
   {
      return new OtherContract
      {
         ItemId = intermediateResult.Item.FirstOrDefault().ItemId,
         ItemName = ((OtherContract)intermediateResult.Other).ItemName
      };
   }


  public class IntermediateResult
   {
      public IItem Other { get; set; }
      public IEnumerable<IItem> Item { get; set; }
   }

   public interface IItem
   {
      int ItemId { get; set; }
   }

   public interface IOther<TContract> : IItem
   {
      string ItemName { get; set; }
   }

   public class Item : IItem
   {
      public int ItemId { get; set; }

      public DateTime CreatedDate { get; set; }
   }

   public class OtherContract : IOther<OtherContract>
   {
      public int ItemId { get; set; }

      public string ItemName { get; set; }

      public string UserName { get; set; }

      public DateTime DateOfBirth { get; set; }

      public DateTime CreatedDate { get; set; }
   }

   public class OtherService
   {
      public IEnumerable<IItem> GetOthers(IEnumerable<int> itemIds)
      {
         return new List<IItem> {
            new OtherContract { ItemId = 1, ItemName = "First" },
            new OtherContract { ItemId = 2, ItemName = "Second" },
            new OtherContract { ItemId = 3, ItemName = "Third" }
         };
      }
   }
}