views:

75

answers:

3

I have two tables:

  • Messages - Amongst other things, has a to_id and a from_id field.
  • People - Has a corresponding person_id

I am trying to figure out how to do the following in a single linq query:

Give me all messages that have been sent to and from person x (idself).

I had a couple of cracks at this.

Not quite right

MsgPeople = (from p in db.people
            join m in db.messages on p.person_id equals m.from_id
            where (m.from_id == idself || m.to_id == idself)
            orderby p.name descending
            select p).Distinct();

This almost works, except I think it misses one case:

"people who have never received a message, just sent one to me"

How this works in my head

So what I really need is something like:

join m in db.messages on (p.people_id equals m.from_id or p.people_id equals m.to_id)

Gets me a subset of the people I am after

It seems you can't do that. I have tried a few other options, like doing two joins:

MsgPeople = (from p in db.people
            join m in db.messages on p.person_id equals m.from_id
            join m2 in db.messages on p.person_id equals m2.to_id
            where (m2.from_id == idself || m.to_id == idself)
            orderby p.name descending
            select p).Distinct();

but this gives me a subset of the results I need, I guess something to do with the order the joins are resolved.

My understanding of LINQ (and perhaps even database theory) is embarrassingly superficial and I look forward to having some light shed on my problem.

+1  A: 

maybe I am missing something but it seems you dont need a join, you say "Give me all messages that have been sent to and from person x (idself)." so if you just want the messages you can work from just he message table, as the id (idself) is known

var MsgPeople from p in AllMessages
                   where p.from_id == idself || p.to_id == idself
                   select p;

ok, try this, you realy need a list of ids for people who sent you messages, and a list of ones that recieved messages from you, then you can select from People any people that exist in that list

var MsgPeople = from p in db.people
                        where (from m in db.messages
                               where m.from_id == selfid
                               select m.to_id).Union(
                        from m in db.messages
                        where m.to_id == selfid
                        select m.from_id).Contains(p.person_id)
                        select p;

another way to do this, one I got from reverse engineering SQL from our DBA when I posed the question to him

 var MsgPeople =  from p in Peoples
     where  p.Person_id != selfid &&
    (from m in Messages
    where m.To_id == selfid select m.From_id).Union(
    from m in Messages
    where m.From_id == selfid select m.To_id).Contains(p.Person_id)
    select p;
Pharabus
Unfortunately, this just gives me messages. I am after the people who have sent messages to and from me.
Cannonade
@Cannonade I have added some more code based on your comment, i think maybe I can improve this using a union, give this a try though, by the way LINQPad is a very good tool for trying this sort of thing
Pharabus
@Pharabus Thanks man ... I'll try it out and let you know how I go.
Cannonade
Getting a couple of errors on that statement.
Cannonade
'System.Linq.IQueryable<long>' does not contain a definition for 'Union'
Cannonade
Instance argument: cannot convert from 'System.Linq.IQueryable<long>' to 'System.Collections.Generic.IEnumerable<long?>'
Cannonade
@Pharabus Any ideas?
Cannonade
@Cannonade strange, works fine for me, I will take another look and post back if i can figure it out
Pharabus
@Cannonade I tested this again and it works fine on my test data, can you edit your question with your copied code so I can see if there is any differences?
Pharabus
@Pharabus Thanks for getting back to me and sorry for the long delay replying (weekend ;) ). I have updated with the query and the member declr where I am putting the results. Seems like I need to store the results in an IEnumerable?
Cannonade
@Cannonade its not the results that is a n issue it is the union between the 2 responses, where do you get db.messages from? it seems that returns an IQueryable<long?> you can make this an emumerable by calling ToList() on it but you need to be carefull as that will cause the query to fire which, depending on how the data is created, may cause multiple DB hits rather than one
Pharabus
A: 

maybe try a union

select all the messages sent by user received by me
union 
select all the messages received by user sent by me
Randy
Thanks. But I am specifically look for help with the linq query syntax :)
Cannonade
+2  A: 

People which sent messages to self or recieved messages from self.

from p in People
where p.SentMessages.Any(m => m.to_id == idself)
  || p.ReceivedMessages.Any(m => m.from_id == idself)
select p;

If your People don't have a these Messages properties, create the associations.


If you want to pull teeth instead... here's the same query without associations.

IQueryable<int> sentQuery = 
  from sent in Messages
  where sent.to_id = idself
  select sent.from_id;

IQueryable<int> receivedQuery =
  from received in Messages
  where received.from_id = idself
  select received.to_id

IQueryble<People> result =
  from p in people
  where System.Linq.Queryable.Concat(receivedQuery, sentQuery)
    .Any(id => id == p.personId)
  select p;
David B
I avoided this because this may cause some misses, the association can only be on one field, so say you choose to_id, if the person in idself has not sent an email it will be missed as the SQL this generates would be SELECT [t0].[Person_id]FROM [people] AS [t0]WHERE EXISTS( SELECT NULL AS [EMPTY] FROM [messages] AS [t1] WHERE (([t1].[to_id] = @p0) OR ([t1].[from_id] = @p1)) AND ([t1].[to_id] = [t0].[Person_id]) ) i think
Pharabus
I missed the fact that there are multiple relationships between People and Messages. Fixed!
David B
yeah that works +1 from me for using the relationships :) also I discovered LINQPad nicely names the associations for me too!
Pharabus
Yeah, associations rock... who really wants to write joins?
David B
@david-b Thanks very much for your answer. I am obviously missing something fundamental here. Associations aside (I will try that as well), but I get a compile error with the teeth pulling example. Similar to the error I get for @Pharabus' answer.
Cannonade
Not sure what to tell you, System.Linq.Queryable has a method called Concat. http://msdn.microsoft.com/en-us/library/bb351755.aspx Maybe try calling it directly? Will edit my answer to show how.
David B
@David-b Sorry for the long delay accepting. I finally got back to this issue and sorted out my problem.
Cannonade