如何将SQL,多对多关系转换为Linq语句

时间:2015-10-23 14:29:32

标签: c# sql sql-server linq

我有一个挑战是将T-SQL查询转换为C#项目中的Linq查询。尽我所能并且为了简单起见,它在以下脚本中表示:

DECLARE @TableA TABLE (ID INT NOT NULL IDENTITY(1, 1)
                        , Data VARCHAR(10) NOT NULL)
DECLARE @TableDetailsA TABLE (ID INT NOT NULL IDENTITY(1, 1)
                                , TableAID INT NOT NULL
                                , TableBID INT NOT NULL)
DECLARE @TableDetailsB TABLE (ID INT NOT NULL IDENTITY(1, 1)
                                , TableAID INT NOT NULL
                                , TableBID INT NOT NULL)
DECLARE @TableB TABLE (ID INT NOT NULL IDENTITY(1, 1)
                        , Data VARCHAR(10) NOT NULL
                        , TableCID INT NOT NULL)
DECLARE @TableC TABLE (ID INT NOT NULL IDENTITY(1, 1)
                        , Data VARCHAR(10) NOT NULL)

INSERT INTO @TableA(Data)
VALUES ('Data set A')
INSERT INTO @TableC(Data)
VALUES ('Data set C')
INSERT INTO @TableB(Data, TableCID)
VALUES ('Data set B', 1)

--INSERT INTO @TableDetailsA(TableAID, TableBID)
--VALUES (1, 1)
INSERT INTO @TableDetailsB(TableAID, TableBID)
VALUES (1, 1)


SELECT A.Data AS [Data A]
        , B.Data AS [Data B]
        , C.Data AS [Data C]
FROM @TableA A
JOIN @TableDetailsA DA
    ON A.ID = DA.TableAID
JOIN @TableB B
    ON DA.TableBID = B.ID
JOIN @TableC C
    ON B.TableCID = C.ID
WHERE B.ID = 1
UNION
SELECT A.Data AS [Data A]
        , B.Data AS [Data B]
        , C.Data AS [Data C]
FROM @TableA A
JOIN @TableDetailsB BA
    ON A.ID = BA.TableAID
JOIN @TableB B
    ON BA.TableBID = B.ID
JOIN @TableC C
    ON B.TableCID = C.ID
WHERE B.ID = 1


SELECT A.Data AS [Data A]
        , B.Data AS [Data B]
        , C.Data AS [Data C]
FROM @TableA A
LEFT JOIN @TableDetailsA DA
    ON A.ID = DA.TableAID
LEFT JOIN @TableDetailsB DB
    ON A.ID = DB.TableAID
JOIN @TableB B
    ON B.ID = ISNULL(DA.TableBID, DB.TableBID)
JOIN @TableC C
    ON B.TableCID = C.ID
WHERE B.ID = 1

通过两个不同的详细信息表TableATableB注意到我在两个表TableDetailsATableDetailsB之间存在多对多关系。在这种情况下,TableDetailsA没有插入数据。

所以基本上,使用linq,在我的C#项目中,我能够像这样复制union语句(如果有的话,忽略linq中的错误......):

var firstQuery =    from ta in repo.TableA
                    join tda in repo.TableDetailsA
                        on ta.ID equals tda.TableAID
                    join tb in repo.TableB
                        on tb.ID equals tda.TableBID
                    join tc in repo.TableC
                        on tb.TableCID = tc.ID
                    select new
                            {
                                ta.Data
                                , tb.Data
                                , tc.Data
                            };

var secondQuery =   from ta in repo.TableA
                    join tdb in repo.TableDetailsB
                        on ta.ID equals tdb.TableAID
                    join tb in repo.TableB
                        on tb.ID equals tdb.TableBID
                    join tc in repo.TableC
                        on tb.TableCID = tc.ID
                    select new
                            {
                                ta.Data
                                , tb.Data
                                , tc.Data
                            };

var unionQuery = firstQuery.Union(secondQuery);

var data = unionQuery.ToList();

但是,我不知道如何复制使用ISNULL(...)加入TableBTableA的第二个SQL语句,我希望能够做到这一点因为这似乎更有效和优雅,并且需要在我的代码中声明更少的变量(虽然我知道这可以在一个var中完成,但我想保持清楚)。

[编辑]

使用Cetin Basoz在答案中给出的第二个查询,我设法构建了以下查询,以生成我正在寻找的结果。但是,我仍然希望找到一种方法让Linq to SQL生成ISNULL(..., ...),如上面的上一个select语句所示。

var firstQuery = from ta in repo.TableA
                    from tda in ta.TableDetailsA.DefaultIfEmpty()
                    from tdb in ta.TableDetailsB.DefaultIfEmpty()
                    where ta.ID == 1
                    select new
                            {
                                TableAID = ta.ID
                                , TableBID = tda.TableB != null
                                                ? ns.TableB.ID
                                                : nsc.TableB.ID
                            };

var secondQuery = from fq in firstQuery
                    join ta in repo.TableA
                        on fq.TableAID equals ta.ID
                    join tb in repo.TableB
                        on fq.TableBID equals tb.ID
                    join tc in repo.TableC
                        on tb.TableCID  equals tc.ID
                    select new
                            {
                                TableAData = ta.Data
                                , TableBData = tb.Data
                                , TableCData = tc.Data
                            };

所以,我们仍在等待我们的冠军解锁这个致命的秘密!

2 个答案:

答案 0 :(得分:0)

var firstQuery = (from tda in TableDetailsA
                  where tda.TableB.ID == 1
                 select new
                 {
                   DataA = tda.TableA.Data,
                   DataB = tda.TableB.Data,
                   DataC = tda.TableB.TableC.Data
                 })
                 .Union(from tdb in TableDetailsB
                   where tdb.TableB.ID == 1
                   select new
                   {
                     DataA = tdb.TableA.Data,
                     DataB = tdb.TableB.Data,
                     DataC = tdb.TableB.TableC.Data
                    });


var secondQuery = from a in TableA
                  from tda in a.TableDetailsA.DefaultIfEmpty()
                  from tdb in a.TableDetailsB.DefaultIfEmpty()
                  select new {
                    DataA = a.Data,
                    DataB = tda.TableB.Data ??  tdb.TableB.Data,
                    DataC = tda.TableB.TableC.Data ??  tdb.TableB.TableC.Data
                  };

答案 1 :(得分:0)

在Cetin Basoz回答的基础上,我找到了一个我满意的解决方案。它不会产生我正在寻找的ISNULL(DA.TableBID, DB.TableBID),但是它确实在join子句中生成了以下内容:CASE WHEN (DA.TableBID IS NULL) THEN DA.TableBID ELSE DB.TableBID END

解锁问题的关键是我在第question的第二个答案中找到的答案,其中tda.TableB.ID通过(int?)表示法强制转换为可以为空的int类型。< / p>

构建Linq查询的代码如下所示:

var firstQuery = from ta in repo.TableA
                    from tda in ta.TableDetailsA.DefaultIfEmpty()
                    from tdb in ta.TableDetailsB.DefaultIfEmpty()
                    where ta.ID == 1
                    select new
                            {
                                TableAID = ta.ID
                                , TableBID = (int?)tda.TableB.ID ?? (int?)tdb.TableB.ID
                            };

var secondQuery = from fq in firstQuery
                    join ta in repo.TableA
                        on fq.TableAID equals ta.ID
                    join tb in repo.TableB
                        on fq.TableBID equals tb.ID
                    join tc in repo.TableC
                        on tb.TableCID equals tc.ID
                    select new
                            {
                                TableAData = ta.Data
                                , TableBData = tb.Data
                                , TableCData = tc.Data
                            };