如何使用LINQ执行动态连接

我有两个系列。 每个集合都包含特定类型的实例。 我需要使用每个集合中实例的一个属性来连接这两个集合。 问题是我将在运行时才知道要用于连接的属性。 那么如何为动态Join编写LINQ查询呢? 这是带有静态连接的LINQ查询的代码。 在下面的示例代码中,您将注意到我正在使用MyTran.MyAmountUSD = YourTran.YourAmountUSD加入两个集合。 但在实际情况中,我将知道在运行时仅使用哪个属性进行连接。 所以有时我可能需要加入MyTran.MyAmountGBP = YourTran.YourAmountGBP。

class MyTran { public int Id { get; set; } public double MyAmountUSD { get; set; } public double MyAmountGBP { get; set; } } class YourTran { public int Id { get; set; } public double YourAmountUSD { get; set; } public double YourAmountGBP { get; set; } } class Program { static void Main(string[] args) { List fMyTranList = new List(); List fYourTranList = new List(); fMyTranList.Add(new MyTran { Id = 1, MyAmountGBP = 100, MyAmountUSD = 1000 }); fMyTranList.Add(new MyTran { Id = 2, MyAmountGBP = 101, MyAmountUSD = 2000 }); fYourTranList.Add(new YourTran { Id = 11, YourAmountGBP=100, YourAmountUSD=1000 }); fYourTranList.Add(new YourTran { Id = 12, YourAmountGBP = 102, YourAmountUSD = 3000 }); var query = from fMyTrans in fMyTranList join fYourTrans in fYourTranList on fMyTrans.MyAmountUSD equals fYourTrans.YourAmountUSD select new { MyId = fMyTrans.Id, YourId = fYourTrans.Id, MyAmtUSD = fMyTrans.MyAmountUSD, MyAmtGBP = fMyTrans.MyAmountGBP, YourAmtUSD = fYourTrans.YourAmountUSD, YourAmtGBP = fYourTrans.YourAmountGBP }; foreach (var fMatch in query) { Console.WriteLine(fMatch); } } } 

解决此问题的第一个线索是使用lamda语法为Join重新编写连接

 var query = fMyTranList.Join(fYourTranList, a => a.MyAmountGBP, b => b.YourAmountGBP, (c,d) => new { MyId = c.Id, YourId = d.Id, MyAmtUSD = c.MyAmountUSD, MyAmtGBP = c.MyAmountGBP, YourAmtUSD = d.YourAmountUSD, YourAmtGBP = d.YourAmountGBP }); 

直播: http : //rextester.com/OGC85986

这应该清楚地表明,制作这种动态需要传递给你的“通用”连接函数3函数

  1. 用于选择LHS密钥的Func函数
  2. 用于选择RHS密钥的Func函数
  3. 用于选择结果的Func函数

所以从那里你可以使用reflection使这一切都有点动态:

 public static class DynamicJoinExtensions { public static IEnumerable DynamicJoin(this IEnumerable myTran, IEnumerable yourTran, params Tuple[] keys) { var outerKeySelector = CreateFunc(keys.Select(k => k.Item1).ToArray()); var innerKeySelector = CreateFunc(keys.Select(k => k.Item2).ToArray()); return myTran.Join(yourTran, outerKeySelector, innerKeySelector, (c, d) => new { MyId = c.Id, YourId = d.Id, MyAmtUSD = c.MyAmountUSD, MyAmtGBP = c.MyAmountGBP, YourAmtUSD = d.YourAmountUSD, YourAmtGBP = d.YourAmountGBP }, new ObjectArrayComparer()); } private static Func CreateFunc(string[] keys) { var type = typeof(TObject); return delegate(TObject o) { var data = new object[keys.Length]; for(var i = 0;i { public bool Equals(object[] x, object[] y) { return x.Length == y.Length && Enumerable.SequenceEqual(x, y); } public int GetHashCode(object[] o) { var result = o.Aggregate((a, b) => a.GetHashCode() ^ b.GetHashCode()); return result.GetHashCode(); } } } 

用于匹配您的示例的用法将是:

 var query = fMyTranList.DynamicJoin(fYourTranList, Tuple.Create("MyAmountGBP", "YourAmountGBP")); 

但由于钥匙是params你可以params地传递:

 var query = fMyTranList.DynamicJoin(fYourTranList, Tuple.Create("MyAmountGBP", "YourAmountGBP"), Tuple.Create("AnotherMyTranProperty", "AnotherYourTranProperty")); 

实例: http : //rextester.com/AAB2452