46

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

Davy Landman
  • 15,109
  • 6
  • 49
  • 73
Lee Atkinson
  • 2,171
  • 3
  • 20
  • 32
  • Why do you think so? You navent values for it after execution? – pocheptsov Apr 27 '09 at 16:50
  • I'm guessing that "Properties" is not the actual string you're passing to Include. This means that you have omitted the most important part of the question. Also, I question why you are using join at all; navigation properties are generally the correct way to traverse relationships in the Entity Framework. – Craig Stuntz Apr 27 '09 at 17:36
  • pocheptsov - I know that Properties hasn't loaded because Proeprties.IsLoaded is false Hi Craig - "Properties" is the correct string. The join is on a different navigation property Items. The join is there because I have a value for a property of an Item object (Collection.ID) yet I want entity that is related to it. Lee – Lee Atkinson Apr 27 '09 at 20:36

5 Answers5

55

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.

Jace Rhea
  • 4,982
  • 4
  • 36
  • 55
Alex James
  • 20,874
  • 3
  • 50
  • 49
  • 1
    Is this still the case? If you use a "select new Item {...};" the .Include() statement does not work? – grimus Mar 17 '11 at 18:57
  • @grimus, wrapping the entire query in an `Include` doesn't seem to currently work when the element type produced by the query is not an entity type. However, the original suggestion of getting the navigation property values inside the `select` clause still seems to work to circumvent this. – Sam Jan 17 '14 at 02:01
20

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") 
sth
  • 222,467
  • 53
  • 283
  • 367
Bryan2010
  • 201
  • 2
  • 2
4

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.

Craig Stuntz
  • 125,891
  • 12
  • 252
  • 273
1

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!

Kinetic
  • 700
  • 8
  • 15
-1

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!

Tomas Aschan
  • 58,548
  • 56
  • 243
  • 402
  • 1
    That is in no way the same thing. Your code will result in n + 1 database queries, where n is the number of rows retrieved. Include results in 1 database query. – Craig Stuntz Apr 27 '09 at 17:35
  • I know I'm not doing the same thing - it was a sloppy way of saying that I'd end up with the same results. The main reason to do this was to see if there would be any Properties loaded this way. If not, the problem is not likely in the .Include() syntax, but rather in the data (where associated Property records might be missing, for example...). – Tomas Aschan Apr 27 '09 at 17:58
  • Hi Tomas If I load the Properties using Properties.Load() it does load them correctly. If i use Include("Properties") on a query that doesn't include a join, e.g.: from e in dc.Entities.Include("Properties") where (e.ID = id) select e; it works OK. Lee – Lee Atkinson Apr 27 '09 at 20:33