views:

44

answers:

1

I've inherited this project from the previous guy, and I'm presently in the process of switching over a number of assembled-from-string SQL queries into LINQ to SQL, which I'm told is what makes life in .NET worth living. However, I'm running into problems filtering a Many-to-Many relationship.

The system is a Project Management intranet affair. The primary object/table of note is Project. Project has many Markets, which uses an intermediary table linkProjectMarkets to link them. There's plenty of other stuff going on, but I can take care of that if I can understand this problem, it's all similar.

In short, I can't for the life of me take a list of Market IDs provided by a set of checkboxes, and filter the list of projects down to those which have a relation to the markets whose ids are provided.

The gist of the schema is:

Project: int id, ...(a bunch of metadata)
linkProjectMarkets: int projectId, int marketId
Market: int id, string summary

All of this, any many, many more, is defined in a dbml file and accompanying generated code. To facilitate access between project and market, project has the following addon method:

public partial class project
{
    public EntitySet<market> markets
    {
        get
        {
            return (EntitySet<market>)from lpm in this._linkProjectMarkets
                                      select lpm.market;
        }
        set
        {
            var set = (EntitySet<market>)from lpm in this._linkProjectMarkets
                                         select lpm.market;
            set.Assign(value);
        }
    }
}

Here's the most recent bit of code I'm attempting to use to filter the set of all projects:

var projects = 
from p in db.projects
select p;

//MarketFilterList is an IEnumerable of market ids created from the checked checkboxes.
if(MarketFilterList.Count() > 0){
    foreach (int mid in MarketFilterList){
        projects = projects.Where(p => p.markets.Select(m => m.id).Contains(mid));
        /*
        Causes "Member access 'Int32 id' of 'PMIS2.market' not legal on type 
        'System.Data.Linq.EntitySet`1[PMIS2.market]."
        */
    }

}

There are a number of other filters to apply, which is why I'm doing it in steps instead of one LINQ statement.

I checked out the designer code; market in linkProjectMarkets is public, and id is public in market. It's my first real C#/.NET experience, so maybe I just don't understand the error message. Apologies if I've given too much, not enough, or not the right information. Please help!

P.S. I already tried LINQ to Entities, and I would have to have a really good reason to try it again.

Edit

Here's something else I tried:

projects = projects.Where(
    p => MarketFilterList.Intersect(from m in p.markets select m.id).Count() > 0
);

That one told me:

Local sequence cannot be used in LINQ to SQL implementation of query operators except the Contains() operator.

Edit again

Here's the solution based on JTew's answer. If anyone has the real reason why project can't access market directly I'd love to hear it.

projects = projects.Where(p => (from lpm in db.linkProjectMarkets
                                where MarketFilterList.Contains(lpm.marketId)
                                select lpm.project).Contains(p));
+1  A: 

I would do the following:

var projMarkLink = from lpm in db.linkProjectMarkets select lpm;

Establish an IQueryable then on subsequent lines add filters:

foreach (int mid in MarketFilterList){
     projMarkLink = projMarkLink.Where(l => l.mid == mid);
}

Then select the result that you need:

return (from lpm in projMarkLink
       from p in projMarkLink.Projects  
       select p).ToList();

You can take this one step further and remove the loop, have a read of this stackoverflow question.

return (from lpm in projMarkLink
       from p in projMarkLink.Projects  
       where lpm.Contains(MarketFilterList)
       select p).ToList();
JTew
I'll give it a shot
Adam Bard
Thanks! Using the linkProjectMarkets table directly did the trick. I'm not too happy about breaking the project<=>market abstraction, but as long as it works.
Adam Bard
In my case the abstraction is through a repository pattern over this code and I would only ever put this in the data layer. The Query starts at linkProjectMarkets because it generates the most optimal sql when executing (at least with EF anyways).If you are happy with the answer please click the tick just below the voting panel beside the answer. Thanks, glad it worked.
JTew
But of course, I was just waiting to see if anyone would do better.
Adam Bard
Oppps, sorry :)
JTew