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