1

Here's my problem: I have a class that have 2 list properties of the same class type (but with some different restriction as on how to be filled), let's say:

public class Team 
{
    [Key]
    public int IDTeam { get; set; }

    public string TeamName { get; set; }

    public List<Programmer> Members { get; set; }

    public List<Programmer> Leaders { get; set; }

    public LoadLists(MyProjectDBContext db) 
    {
        this.Members = db.Programmers.Where(p => p.IDTeam = this.IDTeam 
                 && (p.Experience == "" || p.Experience == null)).ToList();

        this.Leaders = db.Programmers.Where(p => p.IDTeam = this.IDTeam 
                 && (p.Experience != null && p.Experience != "")).ToList();
    }
}

public class Programmer 
{
    [Key]
    public int IDProgrammer { get; set; }

    [ForeignKey("Team")]
    public int IDTeam { get; set; }
    public virtual Team Team { get; set; }

    public string Name { get; set; }

    public string Experience { get; set; }
}

At some point, I need to take a list of Teams, with it's members and leaders, and for this I would assume something like:

return db.Teams
    .Include(m => m.Members.Where(p => p.Experience == "" || p.Experience == null)
    .Include(l => l.Leaders.Where(p => p.Experience != null && p.Experience != "")
    .OrderBy(t => t.TeamName)
    .ToList();

And, of course, in this case I would be assuming it wrong (cause it's not working at all). Any ideas on how to achieve that?

EDIT: To clarify a bit more, the 2 list properties of the team class should be filled according to:

1 - Members attribute - Should include all related proggramers with no experience (proggramer.Experience == null or "");

2 - Leaders attribute - Should include all related proggramers with any experience (programmer.Experiente != null nor "");

EDIT 2: Here's the MyProjectDbContext declaration:

public class MyProjectDBContext : DbContext
{
    public DbSet<Team> Teams { get; set; }
    public DbSet<Programmer> Programmers { get; set; }
}
Atish Dipongkor
  • 9,380
  • 8
  • 45
  • 75
Marcelo Myara
  • 2,368
  • 23
  • 34
  • Could you please define your team seach criteria. Do you need to get a list of teams that consist of only experienced leads and all programmers with no experience??? – Yaugen Vlasau Sep 04 '13 at 11:48
  • Does `db.Team` (or `DataContext.Table` for that matter) even expose the `.Include` method? I can't seem to find it in my project nor in any official MSDN references. Maybe you could try `InsertOnSubmit / InsertAllOnSubmit` or `Attach / AttachAll`. – Marko Gresak Sep 04 '13 at 11:56
  • @maremp `Include` is exposed by the `DbSet` class. http://msdn.microsoft.com/en-us/library/system.data.entity.dbset_methods(v=vs.103).aspx – qujck Sep 04 '13 at 12:07
  • Oh, I thought it's a [DataContext](http://msdn.microsoft.com/en-us/library/system.data.linq.datacontext.aspx) class, the class name misled me. – Marko Gresak Sep 04 '13 at 12:09
  • @maremp MyProjectDBContext is a DbContext class with DbSets on it. – Marcelo Myara Sep 04 '13 at 12:11
  • Yes, I see this now. I'm not used to working with entity framework. But after looking at [DbQuery.Include](http://msdn.microsoft.com/en-us/library/system.data.entity.infrastructure.dbquery.include(v=vs.103).aspx), the method only receives string parameter but you're using lambda expression. (also I'believe you're forgetting `)` at end of each line in last code block). – Marko Gresak Sep 04 '13 at 12:15
  • @maremp, no. It does accept lambda. – Marcelo Myara Sep 04 '13 at 12:18

2 Answers2

1

You are talking about EntityFramework (Linq to entities) right? If so, Include() is a Method of Linq To Entities to include a sub-relation in the result set. I think you should place the Where() outside of the Inlcude().

On this topic you'll find some examples on how to use the Include() method.

So I suggest to add the Include()'s first to include the relations "Members" and "Leaders" and then apply your Where-Statement (can be done with one Where()).

return db.Teams
    .Include("Team.Members")
    .Include("Team.Leaders")
    .Where(t => string.IsNullOrWhitespace(t.Members.Experience) ... )

What is unclear to me is your where criteria and your use-case at all as you are talking of getting a list of Teams with Leaders and Members. May above example will return a list of Teams that match the Where() statement. You can look though it and within that loop you can list its members and leaders - if that is the use-case.

An alternative is something like this:

return db.Members
    .Where(m => string.IsNullOrWhitespace(m.Experience))
    .GroupBy(m => m.Team)

This get you a list of members with no experience grouped by Team. You can loop the groups (Teams) and within on its members. If you like to get each team only once you can add a Distinct(m => m.Team) at the end.

Hope this helps. If you need some more detailed code samples it would help to understand your requirements better. So maybe you can say a few more words on what you expect from the query.

Update:

Just read our edits which sound interesting. I don't think you can do this all in one Linq-To-Entities statement. Personally I would do that on the getters of the properties Members and Leaders which do their own query (as a read-only property). To get performance for huge data amount I would even do it with SQL-views on the DB itself. But this depends a little on the context the "Members" and "Leaders" are used (high frequent etc).

Update 2:

Using a single query to get a table of teams with sublists for members and leaders I would do a query on "Programmers" and group them nested by Team and Experience. The result is then a list of groups (=Teams) with Groups (Experienced/Non-experience) with Programmers in it. The final table then can be build with three nested foreach-Statements. See here for some grouping examples (see the example "GroupBy - Nested").

Community
  • 1
  • 1
Marc
  • 4,165
  • 3
  • 22
  • 32
  • No good. Youre missing the point. If i follow this path i'll be filtering Teams and this is not the point. One team will have relationship with n programmers, some of them with Experience and some not. I want to list ALL teams, but need to put some programmers in one attribute and others on another attribute. hence the where inside the include (which I know ot's wrong but will give the idea of the objective). – Marcelo Myara Sep 04 '13 at 12:17
  • Just read your edits and updated my answer. I would do it with read-only properties "Members" and "Leaders". Each of them does its query and therefore returns a filtered subset of the Programmers relation. – Marc Sep 04 '13 at 12:24
  • that'll do the trick. But, for getting a list of all teams and then on each one fetch two other lists from the Database... Wouldn't this be too heavy on queries? – Marcelo Myara Sep 04 '13 at 12:35
  • Yes... I do agree that it would coast too much... Just read it on your update... Thanks, i'll re-design the solution. :D – Marcelo Myara Sep 04 '13 at 12:37
  • Are you loading all Teams with all Members and Leaders (aka Programmers) anyway? Is this in just in just one use-case so a query will do it or do you need them as general available properties? How much Teams and Programmers we are talking about? – Marc Sep 04 '13 at 13:11
  • I'm not using the actual instances (if that's what you are asking), im listing them just to fill a table. We are probably talking abou 30 or 40 Teams with 2 or 3 hundreds od programmers (of course, the Team-Programmer is just an example, but in my real model there would be 30/40 Team-like classes and 200/300 Programmers-like classes in each. A simple query would do it though... – Marcelo Myara Sep 04 '13 at 13:39
  • Posted Update 2 showing on how I would do that single query for building such a table. – Marc Sep 04 '13 at 14:20
0

Whenever you fetch entities, they will be stored in the context -- regardless of the form they are "selected" in. That means you can fetch the teams along with all the necessary related entities into an anonymous type, like this:

var teams =
    (from team in db.Teams
     select new {
         team,
         relatedProgrammers = team.Programmers.Where(
                                   [query that gets all leaders OR members])
     }).ToList().Select(x => x.team);

It looks like we're throwing away the relatedProgrammers field here, but those Programmer entities are still in memory. So, when you execute this:

foreach (var team in teams) team.LoadLists(db);

...it will populate the lists from the programmers that were already fetched, without querying the database again (assuming db is the same context instance as above).

Note: I haven't tested this myself. It's based on a similar technique shown in this answer.

EDIT - Actually, it looks like your "leaders" and "members" cover all programmers associated with a team, so you should be able to just do Teams.Include(t => t.Programmers) and then LoadLists.

Community
  • 1
  • 1
nmclean
  • 7,266
  • 2
  • 24
  • 37