tags:

views:

150

answers:

4

I'm having a hard time getting the LINQ-syntax.. How can I do this command in a better way?

var user = (from u in context.users
            where u.email.Equals(email)
            select u).Single();
var pinToUser = (from ptu in context.pintousers
                 where ptu.user_id.Equals(user.id)
                 select ptu).Single();
var pin = (from p in context.pins
           where p.idpin.Equals(pinToUser.pin_idpin)
           select p).Single();

return pin;

As you can see, there's a table user, a table pintouser and a table pin. Pintouser references user and pin. Is it possible to write something short like "user.pintouser.pin"? I think I have the navigation properties all set up but I'm not sure how to use them properly or if I could make them better by modifying them.

Thanks for reading

+8  A: 

Use joins to rewrite everything as a single clean query. If I read your queries properly, this should give you the correct result:

var pin = (from u in context.users
          join ptu in context.pintousers on u.id equals ptu.user_id
          join p in context.pins on ptu.pin_idpin equals p.idpin
          where u.email == email
          select p).Single();

Keep in mind, though, that if this query returns anything other than a single result your code will throw an Exception.

If you want to handle the possibility of getting one or no rows then you should use SingleOrDefault().

If you want to handle the possiblity of getting any number of rows then you should really use FirstOrDefault().

Justin Niessner
Wow, that was fast. Thank you, it works and this gave me some insight in using join!
Phil
Original query returns a single pin. Yours returns an IQueryable or some such.
recursive
@recursive - You're right, I was focused on the joins and forgot the call to Single(). Fixed.
Justin Niessner
I think it's supposed to be "context.pins on ptu.pin_idpin equals p.idpin". They are the other way in your example.
Phil
@Phil - Order doesn't matter when specifying the join criteria. It can be written either way.
Justin Niessner
@Justin - I actually had to change it in vs because it complained that they were out of context that way. It worked when I switched the order? Maybe was related to something else though.
Phil
@Phil - Interesting. I updated the answer but I have no idea why that would cause a problem.
Justin Niessner
+2  A: 

You should be using join, as @JustinNiessner points out, but this is another way to write your query.

var user = context.users.Single(u => u.email == email);
var pinToUser = context.pintousers.Single(ptu => ptu.user_id == user.id);
var pin = context.pins.Single(p => p.idpin == pinToUser.pin_idpid);
sshow
+1  A: 

Since you have navigation properties, might as well use them:

Pin pin =
(
  from u in context.Users
  where u.email == email
  from ptu in u.pintousers
  let p = ptu.pin
  select p
).Single();
David B
what does the "let" command do?
Phil
as you might declare a variable inside a loop, creating one variable per loop iteration... "let" allows you to declare a variable for each row in the query.
David B
+2  A: 

Note that if you have your foreign-key relationship set righ in your database, Linq-to-Sql should have the joins for you automatically:

var pin = (from u in context.users 
      where u.email == email 
      select u.pintouser.pin).Single(); 

which means you can reduce this to:

var pin = context.users.Where(u=>u.email == email)
                       .Select(u=>u.pintouser.pin)
                       .Single();

(UPDATE Note: I had originally suggested the following, which is much shorter, but I believe it will cause two round-trips to the database)

var pin = context.users.Single(u=>u.email == email).Single().pintouser.pin;

Now, the .pintouser.pin is safe, because the Single() will always return a user object (or throw an exception).

James Curran