Visual studio 我可以强制自动生成的Linq到SQL类使用外部联接吗?

Visual studio 我可以强制自动生成的Linq到SQL类使用外部联接吗?,visual-studio,visual-studio-2008,linq-to-sql,Visual Studio,Visual Studio 2008,Linq To Sql,假设我有一个Order表,它有一个firstsalersonid字段和一个secondsalersonid字段。这两个都是引用SalesPerson表的外键。对于任何给定的订单,一个或两个销售人员都可以获得订单。换句话说,firstsalersonid永远不能为NULL,但是secondsalersonid可以为NULL 当我将我的订单和销售人员表放到“Linq到SQL类”设计图面上时,类生成器会发现从订单表到销售人员表的两个FK关系,因此,生成的Order类有一个salerson字段和一个sa

假设我有一个
Order
表,它有一个
firstsalersonid
字段和一个
secondsalersonid
字段。这两个都是引用
SalesPerson
表的外键。对于任何给定的订单,一个或两个销售人员都可以获得订单。换句话说,
firstsalersonid
永远不能为
NULL
,但是
secondsalersonid
可以为
NULL

当我将我的
订单
销售人员
表放到“Linq到SQL类”设计图面上时,类生成器会发现从
订单
表到
销售人员
表的两个FK关系,因此,生成的
Order
类有一个
salerson
字段和一个
salerson1
字段(我可以将其重命名为
salerson1
salerson2
,以避免混淆)

因为我总是希望在处理订单时销售人员数据可用,所以我使用
DataLoadOptions.LoadWith
指定在填充订单实例时填充两个销售人员字段,如下所示:

dataLoadOptions.LoadWith<Order>(o => o.SalesPerson1);
dataLoadOptions.LoadWith<Order>(o => o.SalesPerson2);
如果总是有两个salesperson记录,这是有意义的,但因为有时没有第二个salesperson(
SecondSalesorSonid
NULL
),因此
内部联接在这种情况下会导致查询不返回任何记录

我实际上想要的是将第二个
内部联接
更改为
左外部联接
。有没有办法通过类生成器的UI实现这一点?如果没有,我如何才能做到这一点

(请注意,因为我几乎只使用生成的类,如果可以避免的话,我不希望在这一种情况下附加一些东西)



编辑:根据我的评论回复,
secondsalersonid
字段可以为空(在数据库和生成的类中)。

如果将数据库表中的
secondsalersonid
字段设置为空,LINQ to SQL应该正确地构造关联对象,以便生成的SQL语句将执行
左外部联接

更新: 由于该字段可为空,您的问题可能在于显式声明
dataLoadOptions.LoadWith()
。在我当前的项目中,我正在运行一个类似的情况,我有一个订单,但是订单要经过多个阶段。每个阶段对应一个单独的表,其中包含与该阶段相关的数据。我只需检索订单,如果存在相应的数据,则相应的数据会随之出现。我根本不使用
dataLoadOptions
,它做了我需要它做的事情。例如,如果订单有采购订单记录,但没有发票记录,
Order.PurchaseOrder
将包含采购订单数据,
Order.invoice
将为空。我的查询如下所示:

DC.Orders.Where(a => a.Order_ID == id).SingleOrDefault();
我尽量不去微观管理LINQtoSQL…它直接完成了我需要的95%的工作

更新2: 我发现它讨论了DefaultIfEmpty()的用法,以便在子实体不存在时使用null填充它们。我在数据库中使用LINQPad进行了尝试,并将该示例转换为lambda语法(因为我使用的是该语法):

从这条语句中可以看出,GroupJoin表达式关联父表和子表,而SelectMany表达式聚合相关的子记录。关键似乎是DefaultIfEmpty的使用,它强制包含父实体记录,即使没有相关的子记录。(感谢你迫使我进一步深入研究……我想我可能已经找到了一些有用的东西来帮助我完成一份庞大的报告……)

更新3: 如果目标是保持简单,那么看起来您必须在Select()表达式中直接引用这些salesperson字段。首先,必须使用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)
如果尚未建立该关联,则意味着该记录的
MailingList
表中的
ListCompany\u ID
字段等于
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

假设模型设置正确,默认行为实际上是一个左连接

下面是一个稍微匿名的示例,我刚刚在自己的一个数据库上进行了测试:

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);
            }
        }
    }
}
类程序
{
静态void Main(字符串[]参数)
{
使用(TestDataContext=newtestdatacontext())
{
DataLoadOptions dlo=新的DataLoadOptions();
dlo.LoadWith(p=>p.Address);
context.LoadOptions=dlo;

var places=context.places.Where(p=>p.ID>=100&&p.ID=@p0)和([t0].[PlaceID]@Neil:它可以为null(甚至在“链接到SQL类”UI中显示为null)@Neil:是的,如果我不使用LoadWith,那么它只会按需加载销售人员数据。但是,在我的情况下,我将在一个循环中处理数百条订单记录,这意味着需要数百次访问数据库来分别获取每个销售人员记录。这正是我试图避免使用LoadWith的原因。它对f第一个salesperson记录;我正试图让它与第二个一样工作。@Neil:谢谢,我很感谢你抽出时间,但你已经偏离了我要达到的目标,即当我实例化一个Order对象时,Order.SalesPerson1和Order.SalesPerson2字段同时填充,我
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
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);
            }
        }
    }
}
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)