Adding multiple Contains IQueryables to a base IQueryable changes every previous IQueryable - linq

I am spending a good amount of time trying to figure out why Linq2SQL is changing my SQL query. It is rather difficult to explain, and I can't find any reason why this is happening. The low down is it appears that adding multiple contains around an IQueryable seems to overwrite each previous IQueryable Expression. Let me try and explain:
Say you have a Linq2SQL query that provides you the basic framework for query. (something that is the base of all your queries)
I am dynamically adding in parts of the where query (shown as "partQuery" in the examples below). The Expression generated from the where query is correct, and when I add it to the finalQuery -- it still is correct. The problem comes when I add another partQuery to the final query, it seems to overwrite the first query in the final query, but adds a second query into it. (or as shown below, when adding a third query, overwrites the first 2 queries)
Here is some source example:
foreach (var partQuery in whereStatements)
{
finalQuery = finalQuery.Where(
dataEvent => partQuery.Contains(dataEvent.DataEventID)
);
}
The partQuery is of of type IQueryable
finalQuery is the query that will eventually be executed at the SQL server
// the list of the wheres that are sent
var whereStatements = new List<IQueryable<long>>();
var query1 = DataEvent.GetQueryBase(context);
query1 = query1.Where(
dataEvent =>
dataEvent.DataEventKeyID == (short)DataEventTypesEnum.TotalDollarAmount && dataEvent.ValueDouble < -50);
whereStatements.Add(query1.Select(x => x.DataEventID));
var query2 = DataEvent.GetQueryBase(context);
query2 = query2.Where(
dataEvent =>
dataEvent.DataEventKeyID == (short)DataEventTypesEnum.ObjectNumber && dataEvent.ValueDouble == 6);
whereStatements.Add(query2.Select(x => x.DataEventID));
The first where Query (query1) has an expression that turns out like this:
{SELECT [t0].[DataEventID]
FROM [dbo].[DataEvents] AS [t0]
INNER JOIN [dbo].[DataEventAttributes] AS [t1] ON [t0].[DataEventID] = [t1].[DataEventID]
WHERE ([t1].[DataEventKeyID] = #p0) AND ([t1].[ValueDouble] < #p1)
}
Notice that the where line has a ValueDouble "<" #p1 -- less then
and then when added into the final query, it looks like this:
{SELECT [t0].[DataEventID], [t0].[DataOwnerID], [t0].[DataTimeStamp]
FROM [dbo].[DataEvents] AS [t0]
WHERE (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[DataEvents] AS [t1]
INNER JOIN [dbo].[DataEventAttributes] AS [t2] ON [t1].[DataEventID] = [t2].[DataEventID]
WHERE ([t1].[DataEventID] = [t0].[DataEventID]) AND ([t2].[DataEventKeyID] = #p0) AND ([t2].[ValueDouble] < #p1)
)) AND ([t0].[DataOwnerID] = #p2)
}
At this point, the query is still correct. Notice how ValueDouble still has a "<" sign. The problem occurs when I add 2 or more to the query. Here is the expression of the second query in this example:
{SELECT [t0].[DataEventID]
FROM [dbo].[DataEvents] AS [t0]
INNER JOIN [dbo].[DataEventAttributes] AS [t1] ON [t0].[DataEventID] = [t1].[DataEventID]
WHERE ([t1].[DataEventKeyID] = #p0) AND ([t1].[ValueDouble] = #p1)
}
and when added to the final query.. you will notice that the first query is no longer correct.... (And more to come after)
{SELECT [t0].[DataEventID], [t0].[DataOwnerID], [t0].[DataTimeStamp]
FROM [dbo].[DataEvents] AS [t0]
WHERE (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[DataEvents] AS [t1]
INNER JOIN [dbo].[DataEventAttributes] AS [t2] ON [t1].[DataEventID] = [t2].[DataEventID]
WHERE ([t1].[DataEventID] = [t0].[DataEventID]) AND ([t2].[DataEventKeyID] = #p0) AND ([t2].[ValueDouble] = #p1)
)) AND (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[DataEvents] AS [t3]
INNER JOIN [dbo].[DataEventAttributes] AS [t4] ON [t3].[DataEventID] = [t4].[DataEventID]
WHERE ([t3].[DataEventID] = [t0].[DataEventID]) AND ([t4].[DataEventKeyID] = #p2) AND ([t4].[ValueDouble] = #p3)
)) AND ([t0].[DataOwnerID] = #p4)
}
And a bonus to it... after looking at this via the SQL profiler, it appears that it totally dropped the first query, and the two Exists clauses in the final SQl are actually the same query (query2). None of the parameters are actually passed to the SQl server for the first query.
So, in my research of this, it appears that its adding queries to the SQl, but its replacing any existing where exists clause with the last one that was added. To double confirm this.. the exact same code as above, but I added a third query.... and look how it changed.
var query3 = DataEvent.GetQueryBase(context);
query3 = query3.Where(
dataEvent =>
dataEvent.DataEventKeyID != (short)DataEventTypesEnum.Quantity && dataEvent.ValueDouble != 5);
whereStatements.Add(query3.Select(x => x.DataEventID));
I threw in some "!=" to the last part of the query
{SELECT [t0].[DataEventID], [t0].[DataOwnerID], [t0].[DataTimeStamp]
FROM [dbo].[DataEvents] AS [t0]
WHERE (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[DataEvents] AS [t1]
INNER JOIN [dbo].[DataEventAttributes] AS [t2] ON [t1].[DataEventID] = [t2].[DataEventID]
WHERE ([t1].[DataEventID] = [t0].[DataEventID]) AND ([t2].[DataEventKeyID] <> #p0) AND ([t2].[ValueDouble] <> #p1)
)) AND (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[DataEvents] AS [t3]
INNER JOIN [dbo].[DataEventAttributes] AS [t4] ON [t3].[DataEventID] = [t4].[DataEventID]
WHERE ([t3].[DataEventID] = [t0].[DataEventID]) AND ([t4].[DataEventKeyID] <> #p2) AND ([t4].[ValueDouble] <> #p3)
)) AND (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[DataEvents] AS [t5]
INNER JOIN [dbo].[DataEventAttributes] AS [t6] ON [t5].[DataEventID] = [t6].[DataEventID]
WHERE ([t5].[DataEventID] = [t0].[DataEventID]) AND ([t6].[DataEventKeyID] <> #p4) AND ([t6].[ValueDouble] <> #p5)
)) AND ([t0].[DataOwnerID] = #p6)
}
Notice how all three internal queries are now all "<>" where the query above is not that.
Am I totally off my rocker here? Am I missing something so simple that when you tell me I am going to want to pull my fingernails off? I am actually hoping you tell me that, instead of telling me that it looks like a bug in the MS framework (well, we know that happens sometimes).
Any help is greatly appreciated. Maybe I should be sending dynamic query parts to the base query a different way. I am open to ideas.

Without having fully evaluated your examples, the one thing that stands out is this:
foreach (var partQuery in whereStatements)
{
finalQuery = finalQuery.Where(
dataEvent => partQuery.Contains(dataEvent.DataEventID)
);
}
Because of the way this loop is structured, every expression generated in each iteration will eventually use the final value of partQuery--the value that is present when the loop terminates. You probably want this, instead:
foreach (var partQuery in whereStatements)
{
var part = partQuery;
finalQuery = finalQuery.Where(
dataEvent => part.Contains(dataEvent.DataEventID)
);
}
Now, part is the captured variable, and is unique per iteration, and therefore unique per expression. This odd-at-first behavior is by design: see a related question.
Edited: It looks like this is exactly what's causing your problem; the subqueries in the final query are all of the form x <> y, which is the form of the last query added to your whereStatements collection.

Related

Syntax issues with pervasive SQL if statement used to determine left join on clause

Back Story: I am looking to do a left join based on if a condition is true with an if statement. However if it is false, I want to still join the said table in, but with the same columns or with a new set of columns. This query is being wrote for a Pervasive SQL DB.
This query is fairly large, without this particular issue it executes and returns the data sets as expected. Below is a snapshot of the issue I am currently running into..
SELECT A.ONUM, B.JN, C.SEQ, C.PN
From Z.OH
LEFT JOIN OL A ON Z.ONUM = A.ONUM
LEFT JOIN JH B ON A.ONUM = B.SNUM AND A.OLNUM = B.SLNUM AND B.CLSD <> 'Y'
LEFT JOIN JO C ON IF(A.LC <> 'ZY', B.JN = C.JN, LEFT(B.C_PO, 6) = C.JN OR B.JN = C.JN) AND C.OP_T NOT IN ('Z','C')
WHERE Z.OT <> 'T' AND C.PN NOT IN (SELECT X.PN FROM JH X WHERE B.JN = X.JN)
Again, very summarized version with lots of joins/filters/select statement removed.
I am running into issues on the join with the IF statement. Without the if statement, the query executes as expected.
The original join being: B.JN = C.JN AND C.OP_T NOT IN ('Z', 'C')
When executing the query in PCC it would give the following syntax error at the following point: "B.JN << ??? >> = C.JN"
I tried switching over to an OR statement as shown below, but the run time of the query made it an impossible choice.
LEFT JOIN JO C ON
(B.JN = C.JN) OR (A.LC = 'ZY' AND LEFT(B.C_PO, 6) = C.JN)
AND C.OP_T NOT IN ('Z','C')
Checking the documentation, it looks like the query on the if statement is following the correct syntax...
Most simple solution would be to avoid the IF in the WHERE-clause, and do:
SELECT A.ONUM, B.JN, C.SEQ, C.PN
From Z.OH
LEFT JOIN OL A ON Z.ONUM = A.ONUM
LEFT JOIN JH B ON A.ONUM = B.SNUM AND A.OLNUM = B.SLNUM AND B.CLSD <> 'Y'
LEFT JOIN JO C ON (A.LC <> 'ZY' AND B.JN = C.JN) OR (A.LC = 'ZY' AND (LEFT(B.C_PO, 6) = C.JN OR B.JN = C.JN))
AND C.OP_T NOT IN ('Z','C')
WHERE Z.OT <> 'T' AND C.PN NOT IN (SELECT X.PN FROM JH X WHERE B.JN = X.JN)

why does these queries generate different sql?

I have a problem with a linq query in Entity framework. I am querying on a field on a navigation property. The problem is that the generated sql is less than optimal. The example below is simplified, actually I am trying to pass an expression tree, and that is why the second query with the let binding is not a sufficient solution even though the produced sql is what I want.
So to summarize I have two questions:
Why is the generated sql different? And is there any way to produce a sql query which will not create a join for every criteria with expression trees?
Update: I realize that I had Include("Securities") on the first query and not the second when I first posted the question, but it does not change the way the criterias are applied, only which columns are selected.
var qry = db.Positions
.Where(criteria)
.ToList();
var qry1 = (from p in db.Positions
where p.Security.Country == "NO" || p.Security.Country == "US" || p.Security.Country == "GB"
select p).ToList();
var qry2 = (from p in db.Positions
let s = p.Security
where s.Country == "NO" || s.Country == "US" || s.Country == "GB"
select p).ToList();
--qry1
SELECT
[Extent1].* --All columns from tblPositions
FROM [dbo].[tblPositions] AS [Extent1]
LEFT OUTER JOIN [dbo].[tblSecurities] AS [Extent2] ON ([Extent2].[SecurityType] IN (1,2..)) AND ([Extent1].[Security] = [Extent2].[SecuritySeq])
LEFT OUTER JOIN [dbo].[tblSecurities] AS [Extent3] ON ([Extent3].[SecurityType] IN (1,2..)) AND ([Extent1].[Security] = [Extent3].[SecuritySeq])
LEFT OUTER JOIN [dbo].[tblSecurities] AS [Extent4] ON ([Extent4].[SecurityType] IN (1,2..)) AND ([Extent1].[Security] = [Extent4].[SecuritySeq])
LEFT OUTER JOIN [dbo].[tblSecurities] AS [Extent5] ON ([Extent5].[SecurityType] IN (1,2..)) AND ([Extent1].[Security] = [Extent5].[SecuritySeq])
WHERE [Extent2].[Country] = 'NO' OR [Extent3].[Country] = 'US' OR [Extent4].[Country] = 'GB'
--qry2
SELECT
[Extent1].*
FROM [dbo].[tblPositions] AS [Extent1]
LEFT OUTER JOIN [dbo].[tblSecurities] AS [Extent2] ON ([Extent2].[SecurityType] IN (1,2..)) AND ([Extent1].[Security] = [Extent2].[SecuritySeq])
WHERE [Extent2].[Country] IN ('NO','US','GB')
In your first query
var qry1 = (from p in db.Positions.Include("Security")
where p.Security.Country == "NO"
|| p.Security.Country == "US"
|| p.Security.Country == "GB"
select p).ToList();
It seems you assume that Entity Framework has magical powers and can deduce that each of the or statements in the generated expression tree are on the same object, and that it can combine them into a contains. It is, by far, not that smart.
Additionally, the rows returned by both queries are not the same. The second one would also need an include, to include the rows from security (if needed). Otherwise you could remove them from the first query (include only means return rows, it has nothing to do with the ability of the where clause to filter rows).
Or to make it more object oriented and readable.
var allowedCountries = new List<string>() { "NO, "US", "GB" };
var qry1 = (from p in db.Positions
// I'm not sure if this is exactly correct
where p.Security.Country in allowedCountries
select p).ToList();
or Lambda (I'm much more familiar with)
var qry1 = db.Positions
.Where(p => allowedCountries.Contains(p.Security.Country))
.ToList();

LINQ - count from select with join with no group by

Linq is brand new to me so I apologize if this is really stupid.
I am trying to get the count from a multi-table join with where clause, without group by. I've seen examples of group by and will resort to that if need be, but I am wondering if there is a way to avoid it. Is sql my query would look something like this;
SELECT Count(*)
FROM plans p
JOIN organizations o
ON p.org_id = o.org_id
AND o.deleted IS NULL
JOIN orgdata od
ON od.org_id = o.org_id
AND od.active = 1
JOIN orgsys os
ON os.sys_id = od.sys_id
AND os.deleted IS NULL
WHERE p.deleted IS NULL
AND os.name NOT IN ( 'xxxx', 'yyyy', 'zzzz' )
What's the best way to get this?
All you need is to call Count(). You're only counting the number of results. So something like:
var names = new[] { "xxxx", "yyyy", "zzzz" };
var query = from plan in db.Plans
where plan.Deleted == null
join organization in db.Organizations
on plan.OrganizationId equals organization.OrganizationId
where organization.Deleted == null
join orgData in db.OrganizationData
on organization.OrganizationId equals orgData.OrganizationId
where orgData.Active == 1
join os on db.OrganizationSystems
on orgData.SystemId equals os.SystemId
where os.Deleted == null &&
!names.Contains(os.Name)
select 1; // It doesn't matter what you select here
var count = query.Count();

LINQ nested joins

Im trying to convert a SQL join to LINQ. I need some help in getting the nested join working in LINQ.
This is my SQL query, Ive cut it short just to show the nested join in SQL:
select distinct
txtTaskStatus as TaskStatusDescription,
txtempfirstname+ ' ' + txtemplastname as RaisedByEmployeeName,
txtTaskPriorityDescription as TaskPriorityDescription,
dtmtaskcreated as itemDateTime,
dbo.tblTask.lngtaskid as TaskID,
dbo.tblTask.dtmtaskcreated as CreatedDateTime,
convert(varchar(512), dbo.tblTask.txttaskdescription) as ProblemStatement,
dbo.tblTask.lngtaskmessageid,
dbo.tblMessage.lngmessageid as MessageID,
case when isnull(dbo.tblMessage.txtmessagesubject,'') <> '' then txtmessagesubject else left(txtmessagedescription,50) end as MessageSubject,
dbo.tblMessage.txtmessagedescription as MessageDescription,
case when dbo.tblMessage.dtmmessagecreated is not null then dbo.tblMessage.dtmmessagecreated else CAST(FLOOR(CAST(dtmtaskcreated AS DECIMAL(12, 5))) AS DATETIME) end as MessageCreatedDateTime
FROM
dbo.tblAction RIGHT OUTER JOIN dbo.tblTask ON dbo.tblAction.lngactiontaskid = dbo.tblTask.lngtaskid
LEFT OUTER JOIN dbo.tblMessage ON dbo.tblTask.lngtaskmessageid = dbo.tblMessage.lngmessageid
LEFT OUTER JOIN dbo.tblTaskCommentRecipient
RIGHT OUTER JOIN dbo.tblTaskComment ON dbo.tblTaskCommentRecipient.lngTaskCommentID = dbo.tblTaskComment.lngTaskCommentID
ON dbo.tblTask.lngtaskid = dbo.tblTaskComment.lngTaskCommentTaskId
A more seasoned SQL programmer wouldn't join that way. They'd use strictly left joins for clarity (as there is a strictly left joining solution available).
I've unraveled these joins to produce a hierarchy:
Task
Action
Message
TaskComment
TaskCommentRecipient
With associations created in the linq to sql designer, you can reach these levels of the hierarchy:
//note: these aren't outer joins
from t in db.Tasks
let actions = t.Actions
let message = t.Messages
let comments = t.TaskComments
from c in comments
let recipients = c.TaskCommentRecipients
DefaultIfEmpty produces a default element when the collection is empty. Since these are database rows, a default element is a null row. That is the behavior of left join.
query =
(
from t in db.Tasks
from a in t.Actions.DefaultIfEmpty()
from m in t.Messages.DefaultIfEmpty()
from c in t.Comments.DefaultIfEmpty()
from r in c.Recipients.DefaultIfEmpty()
select new Result()
{
TaskStatus = ???
...
}
).Distinct();
Aside: calling Distinct after a bunch of joins is a crutch. #1 See if you can do without it. #2 If not, see if you can eliminate any bad data that causes you to have to call it. #3 If not, call Distinct in a smaller scope than the whole query.
Hope this helps.
SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate], [t0].[RequiredDate], [t0].[ShippedDate], [t0].[ShipVia], [t0].[Freight], [t0].[ShipName], [t0].[ShipAddress], [t0].[ShipCity], [t0].[ShipRegion], [t0].[ShipPostalCode], [t0].[ShipCountry]
FROM [Orders] AS [t0]
LEFT OUTER JOIN ([Order Details] AS [t1]
INNER JOIN [Products] AS [t2] ON [t1].[ProductID] = [t2].[ProductID]) ON [t0].[OrderID] = [t1].[OrderID]
can be write as
from o in Orders
join od in (
from od in OrderDetails join p in Products on od.ProductID equals p.ProductID select od)
on o.OrderID equals od.OrderID into ood from od in ood.DefaultIfEmpty()
select o

Linq-to-entities - Include() method not loading

If I use a join, the Include() method is no longer working, eg:
from e in dc.Entities.Include("Properties")
join i in dc.Items on e.ID equals i.Member.ID
where (i.Collection.ID == collectionID)
select e
e.Properties is not loaded
Without the join, the Include() works
Lee
UPDATE: Actually I recently added another Tip that covers this, and provides an alternate probably better solution. The idea is to delay the use of Include() until the end of the query, see this for more information: Tip 22 - How to make include really include
There is known limitation in the Entity Framework when using Include().
Certain operations are just not supported with Include.
Looks like you may have run into one on those limitations, to work around this you should try something like this:
var results =
from e in dc.Entities //Notice no include
join i in dc.Items on e.ID equals i.Member.ID
where (i.Collection.ID == collectionID)
select new {Entity = e, Properties = e.Properties};
This will bring back the Properties, and if the relationship between entity and Properties is a one to many (but not a many to many) you will find that each resulting anonymous type has the same values in:
anonType.Entity.Properties
anonType.Properties
This is a side-effect of a feature in the Entity Framework called relationship fixup.
See this Tip 1 in my EF Tips series for more information.
Try this:
var query = (ObjectQuery<Entities>)(from e in dc.Entities
join i in dc.Items on e.ID equals i.Member.ID
where (i.Collection.ID == collectionID)
select e)
return query.Include("Properties")
So what is the name of the navigation property on "Entity" which relates to "Item.Member" (i.e., is the other end of the navigation). You should be using this instead of the join. For example, if "entity" add a property called Member with the cardinality of 1 and Member had a property called Items with a cardinality of many, you could do this:
from e in dc.Entities.Include("Properties")
where e.Member.Items.Any(i => i.Collection.ID == collectionID)
select e
I'm guessing at the properties of your model here, but this should give you the general idea. In most cases, using join in LINQ to Entities is wrong, because it suggests that either your navigational properties are not set up correctly, or you are not using them.
So, I realise I am late to the party here, however I thought I'd add my findings. This should really be a comment on Alex James's post, but as I don't have the reputation it'll have to go here.
So my answer is: it doesn't seem to work at all as you would intend. Alex James gives two interesting solutions, however if you try them and check the SQL, it's horrible.
The example I was working on is:
var theRelease = from release in context.Releases
where release.Name == "Hello World"
select release;
var allProductionVersions = from prodVer in context.ProductionVersions
where prodVer.Status == 1
select prodVer;
var combined = (from release in theRelease
join p in allProductionVersions on release.Id equals p.ReleaseID
select release).Include(release => release.ProductionVersions);
var allProductionsForChosenRelease = combined.ToList();
This follows the simpler of the two examples. Without the include it produces the perfectly respectable sql:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name]
FROM [dbo].[Releases] AS [Extent1]
INNER JOIN [dbo].[ProductionVersions] AS [Extent2] ON [Extent1].[Id] = [Extent2].[ReleaseID]
WHERE ('Hello World' = [Extent1].[Name]) AND (1 = [Extent2].[Status])
But with, OMG:
SELECT
[Project1].[Id1] AS [Id],
[Project1].[Id] AS [Id1],
[Project1].[Name] AS [Name],
[Project1].[C1] AS [C1],
[Project1].[Id2] AS [Id2],
[Project1].[Status] AS [Status],
[Project1].[ReleaseID] AS [ReleaseID]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent2].[Id] AS [Id1],
[Extent3].[Id] AS [Id2],
[Extent3].[Status] AS [Status],
[Extent3].[ReleaseID] AS [ReleaseID],
CASE WHEN ([Extent3].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
FROM [dbo].[Releases] AS [Extent1]
INNER JOIN [dbo].[ProductionVersions] AS [Extent2] ON [Extent1].[Id] = [Extent2].[ReleaseID]
LEFT OUTER JOIN [dbo].[ProductionVersions] AS [Extent3] ON [Extent1].[Id] = [Extent3].[ReleaseID]
WHERE ('Hello World' = [Extent1].[Name]) AND (1 = [Extent2].[Status])
) AS [Project1]
ORDER BY [Project1].[Id1] ASC, [Project1].[Id] ASC, [Project1].[C1] ASC
Total garbage. The key point to note here is the fact that it returns the outer joined version of the table which has not been limited by status=1.
This results in the WRONG data being returned:
Id Id1 Name C1 Id2 Status ReleaseID
2 1 Hello World 1 1 2 1
2 1 Hello World 1 2 1 1
Note that the status of 2 is being returned there, despite our restriction. It simply does not work.
If I have gone wrong somewhere, I would be delighted to find out, as this is making a mockery of Linq. I love the idea, but the execution doesn't seem to be usable at the moment.
Out of curiosity, I tried the LinqToSQL dbml rather than the LinqToEntities edmx that produced the mess above:
SELECT [t0].[Id], [t0].[Name], [t2].[Id] AS [Id2], [t2].[Status], [t2].[ReleaseID], (
SELECT COUNT(*)
FROM [dbo].[ProductionVersions] AS [t3]
WHERE [t3].[ReleaseID] = [t0].[Id]
) AS [value]
FROM [dbo].[Releases] AS [t0]
INNER JOIN [dbo].[ProductionVersions] AS [t1] ON [t0].[Id] = [t1].[ReleaseID]
LEFT OUTER JOIN [dbo].[ProductionVersions] AS [t2] ON [t2].[ReleaseID] = [t0].[Id]
WHERE ([t0].[Name] = #p0) AND ([t1].[Status] = #p1)
ORDER BY [t0].[Id], [t1].[Id], [t2].[Id]
Slightly more compact - weird count clause, but overall same total FAIL.
Has anybody actually ever used this stuff in a real business application? I'm really starting to wonder...
Please tell me I've missed something obvious, as I really want to like Linq!
Try the more verbose way to do more or less the same thing obtain the same results, but with more datacalls:
var mydata = from e in dc.Entities
join i in dc.Items
on e.ID equals i.Member.ID
where (i.Collection.ID == collectionID)
select e;
foreach (Entity ent in mydata) {
if(!ent.Properties.IsLoaded) { ent.Properties.Load(); }
}
Do you still get the same (unexpected) result?
EDIT: Changed the first sentence, as it was incorrect. Thanks for the pointer comment!

Resources