有条件加入Linq

有没有办法逐步/有条件地添加连接到查询? 我正在为客户端创建自定义报告工具,并为客户端提供他/她可以选择查询的对象列表。 查询中总会有一个基础对象(“FWOBid”)。

因此,例如,如果客户选择对象“FWOBid”,“FWOItem”和“FWOSellingOption”,我想要这样做:

var query = from fb in fwoBids // if "FWOSellingOption", add this join join so in sellingOptions on fb.Id equals so.BidId // if "FWOItem", add this join join i in fwoItems on fb.Id equals i.FWOBidSection.BidId // select "FWOBid", "FWOItem", and "FWOSellingOption" (everything user has selected) select new { FWOBid = fb, FWOSellingOption = so, FWOItem = i }; 

诀窍是客户可以选择大约6个彼此相关的对象,从而产生许多不同的连接组合。 如果可能的话,我想避免硬编码。

选项是将一些自定义连接与左连接结合使用。

一个体面的TSQL后端不应该在性能方面有任何缺点,因为总是使用所有连接,因为如果条件总是假的,optimers只会删除连接。 但是应该检查出来。

 bool joinA = true; bool joinB = false; bool joinC = true; var query = from fb in fwoBids join so in sellingOptions on new { fb.Id, Select = true } equals new { Id = so.BidId, Select = joinA } into js from so in js.DefaultIfEmpty() join i in fwoItems on new { fb.Id, Select = true } equals new { Id = i.FWOBidSection.BidId, Select = joinB } into ji from i in ji.DefaultIfEmpty() join c in itemsC on new { fb.Id, Select = true } equals new { Id = c.BidId, Select = joinC } select new { FWOBid = fb, FWOSellingOption = so, FWOItem = i, ItemC = c }; 

在Linq查询语法中,这是不可能的,或者查看其他答案几乎不可读。 没有更多的可读性,但另一种可能性是使用扩展方法(一种伪代码):

  bool condition1; bool condition2; List bids = new List(); List sellingOptions = new List(); List items = new List(); var result = bids.Select(x => new {bid = x, sellingOption = (SellingOption)null, item = (Item)null}); if (condition1) result = result.Join( sellingOptions, x => x.bid.Id, x => x.BidId, (x, sellingOption) => new { x.bid, sellingOption, item = (Item)null }); if (condition2) result = result.Join( items, x => x.bid.Id, x => x.BidId, (x, item) => new { x.bid, x.sellingOption, item }); 

只是将其视为一种概念。 它与Peter Duniho基本相同。

问题是,如果你不想立即加入所有选项,如果没有必要,那么它看起来不会那么好。 也许你现在应该尝试加入,不要担心性能。 你有没有测量过它的速度有多慢或多快? 把它想象成“我现在不需要它!”。 如果性能确实是一个问题,那么你可以采取行动。 但如果不是,并且你不知道你是否从未尝试过,那就把它留作你提到的六个连接。

如果没有一个非常好的示例问题,很难提供一个非常好的示例解决方案。 但是,“链接查询”的意思是这样的:

 var query = from x in dba select new { A = x, B = (B)null, C = (C)null }; if ((joinType & JoinType.B) != 0) { query = from x in query join y in dbb on xAId equals y.Id select new { A = xA, B = y, C = xC }; } if ((joinType & JoinType.C) != 0) { query = from x in query join y in dbc on xAId equals y.Id select new { A = xA, B = xB, C = y }; } 

也就是说,根据适当的条件,使用另一个连接查询先前的结果。 请注意,要成功执行此操作,每个查询必须生成相同的类型。 否则,无法将新查询分配给先前的查询结果变量。

请注意,虽然在上面,我只是为每个可能的输入类型都有一个单独的属性,我可以让类型只具有输入列的属性, IdName ,然后是BC类型的Text属性(必须在查询结果类型中以不同方式命名,例如TextBTextC 。 这看起来像这样:

 var query = from x in dba select new { Id = x.Id, Name = x.Name, TextB = (string)null, TextC = (string)null }; if ((joinType & JoinType.B) != 0) { query = from x in query join y in dbb on x.Id equals y.Id select new { Id = x.Id, Name = x.Name, TextB = y.Text, TextC = x.TextC }; } if ((joinType & JoinType.C) != 0) { query = from x in query join y in dbc on x.Id equals y.Id select new { Id = x.Id, Name = x.Name, TextB = x.TextB, TextC = y.Text }; } 

这是一个完整的代码示例,其中包含可运行程序中的上述逻辑:

 class A { public string Name { get; private set; } public int Id { get; private set; } public A(string name, int id) { Name = name; Id = id; } public override string ToString() { return "{" + Name + ", " + Id + "}"; } } class B { public int Id { get; private set; } public string Text { get; private set; } public B(int id, string text) { Id = id; Text = text; } public override string ToString() { return "{" + Id + ", " + Text + "}"; } } class C { public int Id { get; private set; } public string Text { get; private set; } public C(int id, string text) { Id = id; Text = text; } public override string ToString() { return "{" + Id + ", " + Text + "}"; } } [Flags] enum JoinType { None = 0, B = 1, C = 2, BC = 3 } class Program { static void Main(string[] args) { A[] dba = { new A("A1", 1), new A("A2", 2), new A("A3", 3) }; B[] dbb = { new B(1, "B1"), new B(2, "B2"), new B(3, "B3") }; C[] dbc = { new C(1, "C1"), new C(2, "C2"), new C(3, "C3") }; JoinType joinType; while ((joinType = _PromptJoinType()) != JoinType.None) { var query = from x in dba select new { A = x, B = (B)null, C = (C)null }; if ((joinType & JoinType.B) != 0) { query = from x in query join y in dbb on xAId equals y.Id select new { A = xA, B = y, C = xC }; } if ((joinType & JoinType.C) != 0) { query = from x in query join y in dbc on xAId equals y.Id select new { A = xA, B = xB, C = y }; } foreach (var item in query) { Console.WriteLine(item); } Console.WriteLine(); } } private static JoinType _PromptJoinType() { JoinType? joinType = null; do { Console.Write("Join type ['A' for all, 'B', 'C', or 'N' for none]"); ConsoleKeyInfo key = Console.ReadKey(); Console.WriteLine(); switch (key.Key) { case ConsoleKey.A: joinType = JoinType.BC; break; case ConsoleKey.B: joinType = JoinType.B; break; case ConsoleKey.C: joinType = JoinType.C; break; case ConsoleKey.N: joinType = JoinType.None; break; default: break; } } while (joinType == null); return joinType.Value; } } 

我希望这比以前的答案有所改进。

 public class Bids { public int Id { get; set; } public double Price { get; set; } } public class BidSection { public int BidId { get; set; } } public class SellingOptions { public int BidId { get; set; } public int Quantity { get; set; } } public class Item { public int ItemId { get; set; } public BidSection FWOBidSection { get; set; } } public class ConditionalJoin { public bool jOpt1 { get; set; } public bool jOpt2 { get; set; } public ConditionalJoin(bool _joinOption1, bool _joinOption2) { jOpt1 = _joinOption1; jOpt2 = _joinOption2; } public class FBandSo { public Bids FWOBids { get; set; } public SellingOptions FWOSellingOptions { get; set; } } public class FBandI { public Bids FWOBids { get; set; } public Item FWOItem { get; set; } } public void Run() { var fwoBids = new List(); var sellingOptions = new List(); var fwoItems = new List(); fwoBids.Add(new Bids() { Id = 1, Price = 1.5 }); sellingOptions.Add(new SellingOptions() { BidId = 1, Quantity = 2 }); fwoItems.Add(new Item() { ItemId = 10, FWOBidSection = new BidSection() { BidId = 1 } }); IQueryable fb = fwoBids.AsQueryable(); IQueryable so = sellingOptions.AsQueryable(); IQueryable i = fwoItems.AsQueryable(); IQueryable FBandSo = null; IQueryable FBandI = null; if (jOpt1) { FBandSo = from f in fb join s in so on f.Id equals s.BidId select new FBandSo() { FWOBids = f, FWOSellingOptions = s }; } if (jOpt2) { FBandI = from f in fb join y in i on f.Id equals y.FWOBidSection.BidId select new FBandI() { FWOBids = f, FWOItem = y }; } if (jOpt1 && jOpt2) { var query = from j1 in FBandSo join j2 in FBandI on j1.FWOBids.Id equals j2.FWOItem.FWOBidSection.BidId select new { FWOBids = j1.FWOBids, FWOSellingOptions = j1.FWOSellingOptions, FWOItems = j2.FWOItem }; } } }