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 loadOp开发者_Go百科tions = 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?
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.
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).
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();
//...
}
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
精彩评论