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

发布于 2024-09-28 01:26:10 字数 1057 浏览 4 评论 0原文

我有一个非常简单的设置。表“Node”具有可为空的外键“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?

I have a very simple set-up. Table "Node" has a nullable foreign key "ObjectId." This is represented in my database model with a one-to-many association. Now, I want to run a query that gives me all Node-Objects with a particular object id. In straight SQL, this is very easy:

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

But now I want to do the same thing in 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();
...

The frustrating thing is that LINQ always generates a LEFT OUTER JOIN for this query and nothing I've tried makes difference.

On the face of it, this seems to make sense. The ObjectId foreign key is nullable, so some nodes won't have an associated object. But in my query, I'm supplying an object id. I'm not interested in nodes without an associated object.

In this case, an INNER JOIN is the right thing to do, but how do I convince LINQ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

陌路黄昏 2024-10-05 01:26:10

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

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

I think you are just going to have to let it be a left outer join. I imagine when the expression tree is being transformed to SQL, the join and the equality predicate are considered seperate parts of the resultant query. In other words, the LEFT OUTER JOIN is just there because of the fact that you are joining on a nullable foreign key, and the equality part is written in afterwards (so to speak).

Does it really matter that it's not translating how you want it? The fact that you don't always get the most efficient query possible is kind of an accepted tradeoff when you use LINQ to SQL. Most of the time the queries are pretty efficient if you aren't doing anything crazy, and if you really think it's going to impact performance or something, you can always write a stored procedure LINQ to SQL can use.

香草可樂 2024-10-05 01:26:10
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 的值)。

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

You misunderstand the purpose of this statement. It does not filter the result in any way. It does not get translated into sql that can filter the result in any way. INNER JOIN will filter the result set, and LEFT JOIN won't, so LEFT JOIN is the correct choice.

If you want to filter the nodes, you should use a query that includes your filter criteria:

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

Consider the difference between our queries when objectId is null (the query translator does not inspect objectId's value).

不美如何 2024-10-05 01:26:10

我最终确实找到了一个很好的解决方案。答案是简单地摆脱 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();

    //...
}

I did eventually find a good solution to this. The answer is to simply get LINQ to SQL out of the way. Like so:

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();

    //...
}
陌上芳菲 2024-10-05 01:26:10

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

我会像这样强制连接:

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

Seems very complicated for what I think you want.

I would force the joins like this:

from n in context.Nodes join o in context.Objects on n.ObjectId 
    equals o.Object_id select n
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文