2010-03-14 38 views
2

比方说,我有一个Order表,它有一个FirstSalesPersonId字段和一个SecondSalesPersonId字段。这两个都是引用SalesPerson表的外键。对于任何给定的订单,可以将一个或两个销售人员记入订单。换句话说,FirstSalesPersonId永远不可能是NULL,但SecondSalesPersonId可以是NULL我可以强制自动生成的Linq-to-SQL类使用OUTER JOIN吗?

当我将我的OrderSalesPerson表到“LINQ to SQL类”设计图面,该类建设者斑点从Order表两个FK关系到SalesPerson表,并因此产生Order类有一个SalesPerson场和一个SalesPerson1字段(为了避免混淆,我可以将其重命名为SalesPerson1SalesPerson2)。

因为我总是希望有营业员可用数据,每当我处理订单,我使用DataLoadOptions.LoadWith指定两个营业员字段填充填充了单实例时,如下所示:

dataLoadOptions.LoadWith<Order>(o => o.SalesPerson1); 
dataLoadOptions.LoadWith<Order>(o => o.SalesPerson2); 

我遇到的问题是,LINQ到SQL是使用类似下面的SQL来加载顺序:

SELECT ... 
FROM Order O 
INNER JOIN SalesPerson SP1 ON SP1.salesPersonId = O.firstSalesPersonId 
INNER JOIN SalesPerson SP2 ON SP2.salesPersonId = O.secondSalesPersonId 

这将使意义,如果总有两个销售人员记录,但由于有时没有第二个销售人员(secondSalesPersonIdNULL),因此INNER JOIN会导致查询在该情况下不返回任何记录。

我在这里有效地想要将第二个INNER JOIN更改为LEFT OUTER JOIN。有没有办法通过用于类生成器的用户界面来执行此操作?如果不是,我还能如何实现这一目标? (请注意,因为我几乎全部使用生成的类,所以如果我可以避免的话,我宁愿没有在这一种情况下加上一些东西)。


编辑:按我的评论回复中,SecondSalesPersonId可空(在数据库中,并在生成的类)。

回答

1

的默认行为实际上是一个LEFT JOIN,假设你已经正确地建立模型。

这里的,我只是在我自己的数据库的一个测试一个稍微匿名例如:

class Program 
{ 
    static void Main(string[] args) 
    { 
     using (TestDataContext context = new TestDataContext()) 
     { 
      DataLoadOptions dlo = new DataLoadOptions(); 
      dlo.LoadWith<Place>(p => p.Address); 
      context.LoadOptions = dlo; 

      var places = context.Places.Where(p => p.ID >= 100 && p.ID <= 200); 
      foreach (var place in places) 
      { 
       Console.WriteLine(p.ID, p.AddressID); 
      } 
     } 
    } 
} 

这仅仅是一个简单的测试,打印出的地方,他们的地址ID的列表。这是出现在分析器查询文本:

SELECT [t0].[ID], [t0].[Name], [t0].[AddressID], ... 
FROM [dbo].[Places] AS [t0] 
LEFT OUTER JOIN (
    SELECT 1 AS [test], [t1].[AddressID], 
     [t1].[StreetLine1], [t1].[StreetLine2], 
     [t1].[City], [t1].[Region], [t1].[Country], [t1].[PostalCode] 
    FROM [dbo].[Addresses] AS [t1] 
) AS [t2] ON [t2].[AddressID] = [t0].[AddressID] 
WHERE ([t0].[PlaceID] >= @p0) AND ([t0].[PlaceID] <= @p1) 

这不正是一个非常漂亮的查询(你猜我的一样好,什么是1 as [test]是一回事),但它是明确一个LEFT JOIN并没有表现出你似乎遇到的问题。这只是使用生成的类,我没有做任何改变。

请注意,我也测试了这种双重关系(即单个Place有两个Address引用,一个是空的,一个不是),我得到完全相同的结果。第一个(不可为空)变成了INNER JOIN,第二个变成了LEFT JOIN

它必须是你的模型中的东西,比如改变第二个参考的可空性。我知道你说它配置为空,但也许你需要仔细检查?如果它肯定是可以空的,那么我建议你发布你的完整模式和DBML,这样有人可以尝试重现你所看到的行为。

0

如果您使数据库表中的secondSalesPersonId字段为空,LINQ-to-SQL应正确构造Association对象,以便生成的SQL语句将执行LEFT OUTER JOIN

UPDATE: 由于该字段为空,您的问题可能是在明确声明dataLoadOptions.LoadWith<>()。我在当前有一个订单的项目中运行的情况类似,但订单经历了多个阶段。每个阶段都对应一个单独的表,其中包含与该阶段相关的数据。我只是简单地检索订单,如果存在的话,相应的数据也随之而来。我根本不使用dataLoadOptions,而是按照我需要的做。例如,如果订单具有采购订单记录但没有发票记录,则Order.PurchaseOrder将包含采购订单数据,并且Order.Invoice将为空。我的查询看起来是这样的:

DC.Orders.Where(a => a.Order_ID == id).SingleOrDefault(); 

我尽量不要微观LINQ到SQL ......它的什么,我需要开箱的95%。

更新2: 我发现this post,讨论,以便在使用DefaultIfEmpty()以NULL填充子实体如果他们不存在。我LINQPad试过了,在我的数据库,并完成转换的例子lambda语法(因为这是我用的):

ParentTable.GroupJoin 
(
    ChildTable, 
    p => p.ParentTable_ID, 
    c => c.ChildTable_ID, 
    (p, aggregate) => new { p = p, aggregate = aggregate } 
) 
.SelectMany (a => a.aggregate.DefaultIfEmpty(), 
    (a, c) => new 
    { 
     ParentTableEntity = a.p, 
     ChildTableEntity = c 
    } 
) 

从我从这句话弄明白,该群组加入表达涉及父和子表,而SelectMany表达式聚合相关的子记录。关键似乎是使用DefaultIfEmpty,即使没有相关的子记录,也会强制包含父实体记录。 (感谢引人注目我进一步挖成这样...我想我可能已经找到了一些有用的东西,以帮助一个相当庞大的报告我已经在我的管线了...)

更新3: 如果目标是保持简单,那么看起来您必须直接在Select()表达式中引用这些销售人员字段。首先使用LoadWith <>()的原因是因为这些表没有在查询语句中的任何位置被引用,所以LINQ引擎不会自动将该信息拉入。

作为例如,给出这样的结构:

MailingList    ListCompany 
===========    =========== 
List_ID (PK)    ListCompany_ID (PK) 
ListCompany_ID (FK)  FullName (string) 

我想与特定的邮件列表相关的公司的名称:

MailingLists.Where(a => a.List_ID == 2).Select(a => a.ListCompany.FullName) 

如果该协会尚未作出,这意味着ListCompany_ID FIEL d在MailingList表中该记录等于null,这是由LINQ引擎生成的结果SQL:

SELECT [t1].[FullName] 
FROM [MailingLists] AS [t0] 
LEFT OUTER JOIN [ListCompanies] AS [t1] ON [t1].[ListCompany_ID] = [t0].[ListCompany_ID] 
WHERE [t0].[List_ID] = @p0 
+0

@Neil:它*为*可空(甚至在“链接到SQL类”UI中显示为空)。 – 2010-03-14 22:12:30

+0

@尼尔:是的,如果我不使用LoadWith,那么它只会按需加载销售人员数据。但是,在我的情况下,我将在一个循环中处理数百个订单记录,这将意味着数百次访问数据库以单独获取每个销售人员记录。这正是我试图用LoadWith避免的。它与第一个销售人员记录一起工作;我试图让它与第二个一起工作。 – 2010-03-15 09:28:21

+0

@Neil:谢谢,我非常感谢你的时间,但你已经有点离开了我想要去的地方,那就是当我实例化一个Order对象时,Order.SalesPerson1和Order.SalesPerson2字段在相同的数据库查询中同时填充。我不想编写SQL来执行它(或LINQ伪SQL),我只是希望它能像“简单”情况下那样工作。这似乎并不合理,因为这不是一个复杂的情况。 – 2010-03-18 11:32:07

相关问题