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

Time:12-29

I've created a generic function to build a flat list, it takes tow arguments one of them is list of items and other one is the DTO for a return type, I've made left join to combine the items list with the other list and get the result of them

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; }
    }

In that case it works but I don't know the type that comes in item list just I knew they share ItemId , and the other fields in DTO returns with null , I've tried to return it as anonymous type but in that case join not works , So I need to return the new joined data in a DTO whatever fields in there without using anonymous type.

Note : All passed arguments also inherits from 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; }
    } 

I need the final result to be :

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; }
    }

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

CodePudding user response:

This fiddle posted by @MikeHofer is the best solution till now , hope this help someone else .

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" }
         };
      }
   }
}
  • Related