如何强制LINQ to SQL在可以为空的外键上执行INNER JOIN?

时间:2010-10-20 20:04:13

标签: c# sql linq inner-join

我的设置非常简单。表“节点”具有可为空的外键“ObjectId”。这在我的数据库模型中表示,具有一对多关联。现在,我想运行一个查询,它给我所有具有特定对象ID的节点对象。在直接的SQL中,这很容易:

SELECT Node.*, Object.*
FROM Node INNER JOIN Object
    ON Node.ObjectId = Object.ObjectId
WHERE Node.ObjectId = @objectId

但现在我想在LINQ to SQL中做同样的事情:

private static Func<MyDataContext, string, IQueryable<DataNode>> _queryGet =
        CompiledQuery.Compile(
            (MyDataContext context, string objectId) =>
                (from node in context.DataNodes
                 where node.ObjectId == objectId
                 select node));

var loadOptions = new DataLoadOptions();
loadOptions.LoadWith<DataNode>(node => node.DataObject);
context.LoadOptions = loadOptions;

DataNode node = _queryGet.Invoke(context, objectId).FirstOrDefault();
...

令人沮丧的是,LINQ 总是为此查询生成LEFT OUTER JOIN,而我尝试的任何内容都没有什么区别。

从表面上看,这似乎有道理。 ObjectId外键可以为空,因此某些节点不具有关联对象。但在我的查询中,我正在提供一个对象ID。我对没有关联对象的节点不感兴趣。

在这种情况下,INNER JOIN是正确的做法,但我如何说服LINQ?

4 个答案:

答案 0 :(得分:2)

我认为你只需要让它成为左外连接。我想当表达式树被转换为SQL时,连接和等式谓词被认为是结果查询的单独部分。换句话说,LEFT OUTER JOIN就在那里,因为你加入了一个可以为空的外键,并且之后写了相等的部分(可以这么说)。

它没有翻译你想要的东西真的很重要吗?当您使用LINQ to SQL时,您并不总能获得最有效的查询这一事实是一种可接受的权衡。大多数情况下,如果你没有做任何疯狂的事情,查询是非常有效的,如果你真的认为它会影响性能或其他什么,你总是可以编写LINQ to SQL可以使用的存储过程

答案 1 :(得分:1)

我最终找到了一个很好的解决方案。答案是简单地让LINQ to SQL脱离困境。像这样:

using (MyDataContext context = CreateDataContext())
{
    // Set the load options for the query (these tell LINQ that the
    // DataNode object will have an associated DataObject object just
    // as before).
    context.LoadOptions = StaticLoadOptions;

    // Run a plain old SQL query on our context.  LINQ will use the
    // results to populate the node object (including its DataObject
    // property, thanks to the load options).
    DataNode node = context.ExecuteQuery<DataNode>(
        "SELECT * FROM Node INNER JOIN Object " +
        "ON Node.ObjectId = Object.ObjectId " +
        "WHERE ObjectId = @p0",
        objectId).FirstOrDefault();

    //...
}

答案 2 :(得分:0)

loadOptions.LoadWith<DataNode>(node => node.DataObject); 

你误解了这句话的目的。它不会以任何方式过滤结果。它不会被转换为可以以任何方式过滤结果的sql。 INNER JOIN将过滤结果集,而LEFT JOIN则不会,因此LEFT JOIN是正确的选择。

如果要过滤节点,则应使用包含过滤条件的查询:

from node in context.DataNodes  
where node.ObjectId == objectId  
where node.DataObject != null
select node

当objectId为null(查询转换器不检查objectId的值)时,请考虑我们的查询之间的差异。

答案 3 :(得分:0)

对于我认为你想要的东西似乎非常复杂。

我会像这样强制加入:

from n in context.Nodes join o in context.Objects on n.ObjectId 
    equals o.Object_id select n