有没有办法逐步/有条件地为查询添加联接?我正在为客户创建一个自定义报告工具,并为客户端提供他/她可以选择查询的对象列表。查询中总会有一个基础对象(“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个彼此相关的对象,从而产生许多不同的连接组合。如果可能的话,我想避免硬编码。
答案 0 :(得分:4)
在Linq查询语法中,这是不可能的,或者查看其他答案几乎无法读取。没有更多的可读性,但另一种可能性是使用扩展方法(一种伪代码):
bool condition1;
bool condition2;
List<Bid> bids = new List<Bid>();
List<SellingOption> sellingOptions = new List<SellingOption>();
List<Item> items = new List<Item>();
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基本相同。
问题是,如果你不想在没有必要的情况下立即加入所有选项,那么它看起来不会很好。也许你现在应该尝试加入,不要担心性能问题。你有没有测量过它可能有多慢或多快?把它想象成&#34;我现在不需要它!&#34;。如果性能确实是一个问题,那么你可以采取行动。但如果不是,并且你不知道你是否从未尝试过,那就把它留作你提到的六个连接。
答案 1 :(得分:3)
On选项是将一些自定义连接与左连接结合使用。
一个体面的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
};
答案 2 :(得分:3)
如果没有一个非常好的示例问题,很难提供一个非常好的示例解决方案。但是,“链接查询”的意思是这样的:
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 x.A.Id equals y.Id
select new { A = x.A, B = y, C = x.C };
}
if ((joinType & JoinType.C) != 0)
{
query = from x in query
join y in dbc on x.A.Id equals y.Id
select new { A = x.A, B = x.B, C = y };
}
即,根据适当的条件,使用另一个连接查询先前的结果。请注意,要成功执行此操作,每个查询必须生成相同的类型。否则,无法将新查询分配给先前的查询结果变量。
请注意,虽然在上面,我只是为每个可能的输入类型都有一个单独的属性,但我可以让类型只具有输入列的属性,Id
,Name
和然后Text
和B
类型的C
属性(在查询结果类型中必须以不同的名称命名,例如TextB
和TextC
)。这看起来像这样:
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 x.A.Id equals y.Id
select new { A = x.A, B = y, C = x.C };
}
if ((joinType & JoinType.C) != 0)
{
query = from x in query
join y in dbc on x.A.Id equals y.Id
select new { A = x.A, B = x.B, 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;
}
}
答案 3 :(得分:1)
我希望这比以前的答案有所改进。
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<Bids>();
var sellingOptions = new List<SellingOptions>();
var fwoItems = new List<Item>();
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<Bids> fb = fwoBids.AsQueryable();
IQueryable<SellingOptions> so = sellingOptions.AsQueryable();
IQueryable<Item> i = fwoItems.AsQueryable();
IQueryable<FBandSo> FBandSo = null;
IQueryable<FBandI> 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
};
}
}
}