Linq to Sql - How to get data from second level table - c#

I'm newbie to linq to sql, just trying to understand what type of queries I can handle with linq.
Here is my database scheme,
I want to get all customers of a specific user and this is what I've done,
var userId = 4;
var companies = from c in db.Company
where c.UserId == userId
select c.Id;
var costumers = from c in db.Customers
where companies.Contains(c.CompanyId)
select c;
I'm just wondering whether it's a nice approach and is there any better method to handle this type of queries?

Use can also get customers by this way also:
var result = db.Customers.Join(
db.Company, customer => customer.CompanyId, comp => comp.Id, (customer, comp)
=> new { customer, comp }).Where(#t => #t.comp.UserId == 4)
.Select(#t => #t.customer);

You can also keep it simple like this.
select * from db.Customers Cus
inner join db.company Com on Com.Id = Cus.CompanyId
where Com.UserId= userId

Contains is the equivalent of IN in SQL and your Linq statement will be translated to a SQL statement. So I can't really see another way that will give you better performance with Linq. If you want to use less code you can maybe try the following instead:
var companies = db.Companies.Where(x=> x.UserId == userid).Select(x=>x.Id);
var customers = db.Customers.Where(x=> companies.Contains(x.CompanyId));

Related

Equivalent LINQ query for SQL query not resulting in expected results

I am trying to write a LINQ query equivalent to below SQL
SELECT DISTINCT m.*,rm.RoleId FROM dbo.Menu m
INNER JOIN dbo.RoleMenu rm on m.Id=rm.MenuId
INNER JOIN dbo.RoleUser ru on rm.RoleId=ru.RoleId
WHERE ru.UserName='dd#dd.com' and m.Url='/dashboard#/pm'
I came with the below query which is not returning the expected output
var auth = _context.RoleUsers.Where(
x => x.Role.MenuRoles.FirstOrDefault().Menu.Url == pagePermissions.Url
&& x.UserName == pagePermissions.UserName).Count()
May I know a better way to do this?
Your sql looks at all the menus related to a role user, but your Linq is only looking at the first one. I think you want x.Role.MenuRoles.Any(mr => mr.Menu.Url == pagePermissions.Url). But then you're also doing a Count on the matching users instead of selecting the menus that match that url. A closer translation would be.
var results = (from m in _context.Menus
from rm in m.RoleMenus
from ru in rm.RoleUsers
where m.Url == pagePermissions.Url
&& u.UserName == pagePermissions.UserName
select new { Menu = m, rm.RoleId }).Distinct();
You may have to adjust some of the navigation properties as I was just guessing at them. They usually are pluralizations of the tables, but I see in your Linq that you have MenuRoles instead of RoleMenus.

Linq Best way to Set the inner property

I am trying to pull the results from the database and set the child property while selecting using the Linq (EF V5.0). The reason I am doing this is because there is no relation in the database to use include..
var lamdaResult = from u in model.Entity_Users
join s in model.Entity_Staff on u.UserID equals s.ST_UserID
select new { u, s };
return lamdaResult.Select(x => x.u.Staff = x.s; return x.u;).FirstOrDefault();
I am learning Linq.. the above expression is giving me error.. can someone help me the best way to set the child property...
I could also do this.. but I am wondering is there any better way to fulfill the same result instead of following 2 expressions
var user=null;
var lamdaResult = from u in model.Entity_Users
join s in model.Entity_Staff on u.UserID equals s.ST_UserID
select new { u, s };
user = lamdaResult.Select(x => x.u).FirstOrDefault();
user.Staff = lamdaResult.Select(x => x.s).FirstOrDefault();
Linq is for querying, not for mutating objects. You'll need to use something other than LINQ to do the mutation, generally a foreach is appropriate, although given that you only have a single item, there's no need for even that:
var item = (from u in model.Entity_Users
join s in model.Entity_Staff on u.UserID equals s.ST_UserID
select new { User = u, Staff = s })
.FirstOrDefault();
item.User.Staff = item.Staff;
return item.User;

LINQ Projected Filtering C#

I want to filter my LINQ query based on an included table but am having some trouble.
Here is the original statement, which works:
return
this.ObjectContext.People.
Include("Careers").
Include("Careers.Titles").
Include("Careers.Titles.Salaries");
Now I'm trying to filter on Careers using projected filtering but am having trouble. It compiles but it leaves out the Titles and Salaries tables, which causes runtime errors, and I can't seem to add those tables back in:
var query1 = (
from c in
this.ObjectContext.People.
Include("Careers").
Include("Careers.Titles").
Include("Careers.Titles.Salaries")
select new
{
c,
Careers = from Careers in c.Careers
where Careers.IsActive == true
select Careers
});
var query = query1.AsEnumerable().Select(m => m.c);
return query.AsQueryable();
How can I include the titles and salaries tables in the filtered query?
You can simplify your query considerably, which should resolve your issue. I'm assuming that you want all people with at least 1 active career:
var query =
from c in
this.ObjectContext.People.
Include("Careers").
Include("Careers.Titles").
Include("Careers.Titles.Salaries")
where c.Careers.Any(c => c.IsActive);
return query;
I would try something like,
var query = from p in ObjectContext.People
join c in ObjectContext.Careers on p equals c.Person
where c.IsActive
select p;

How to add a where clause on a linq join (lambda)?

I have two database tables Contact (Id, Name, ...) and ContactOperationalPlaces (ContactId, MunicipalityId), where a contact can be connected to several ContactOperationalPlaces.
What I'm trying to do is to build a query (ASP .NET, C#) with IQueryable, that only selects all the contacts that exists in the ContactOperationalPlaces table, with a given MunicipalityId.
The sql query looks like this:
select * from Contacts c
right join ContactOperationPlaces cop on c.Id = cop.ContactId
where cop.MunicipalityId = 301;
With linq it would look something like this:
//_ctx is the context
var tmp = (from c in _ctx.Contacts
join cop in _ctx.ContactOperationPlaces on c.Id equals cop.ContactId
where cop.MunicipalityId == 301
select c);
So, I know how to do this if the point was to select all of this at once, unfortunately it's not. I'm building a query based on user input, so I don't know all of the selection at once.
So this is what my code looks like:
IQueryable<Contacts> query = (from c in _ctx.Contacts select c);
//Some other logic....
/*Gets a partial name (string nameStr), and filters the contacts
so that only those with a match on names are selected*/
query = query.Where(c => c.Name.Contains(nameStr);
//Some more logic
//Gets the municipalityId and wants to filter on it! :( how to?
query = query.where(c => c.ContactOperationalPlaces ...........?);
The difference with the two where statements is that with the first one, each contact has only one name, but with the latter a contact can contain several operational places...
I have managed to find one solution, but this solution gives me an unidentyfied object, that contains both of the tables. And I don't know how to proceed with it.
query.Join(_ctx.ContactOperationPlaces, c => c.Id, cop => cop.ContactId,
(c, cop) => new {c, cop}).Where(o => o.cop.municipalityId == 301);
The object returned from this expression is System.Linq.Iqueryable<{c:Contact, cop:ContactOperationalPlace}>, and it can't be cast to Contacts...
So, that's the issue. The answer is probably pretty simple, but I just can't find it...
You create an anonymous type with both objects before your where clause and filter it on ContactOperationPlaces value. You just have to select the Contact after that.
query.Join(_ctx.ContactOperationPlaces, c => c.Id, cop => cop.ContactId,
(c, cop) => new {c, cop}).Where(o => o.cop.municipalityId == 301)
.Select(o => o.c)
.Distinct();
You don't need to return new objects in the result selector function. The delegate provides both variables so you can choose one or the other, or some other variation (which would require a new object). Try this:
query.Join(_ctx.ContactOperationPlaces, c => c.Id, cop => cop.ContactId,
(c, cop) => c).Where(o => o.cop.municipalityId == 301);
can you just cast it to var and try to use intellisense on it?
var myCast = query.Join(_ctx.ContactOperationPlaces, c => c.Id, cop => cop.ContactId,
(c, cop) => new {c, cop}).Where(o => o.cop.municipalityId == 301);
Just a thought
I think it would be much easier if you start this as 2 different queries, then combine them. I'm assuming the relation is Contact (1 <-> many) Contactoperationplaces ? And in the end, you will be showing 1 item per Contactoperationplaces, not 1 item per Contact?
Do it like this:
IQueryable<Contacts> query = (from c in _ctx.Contacts select c);
...
query = query.Where(x=> x.Name.ToLower().Contains(nameStr.ToLower());
...
IQueryable<ContactOperationPlaces> query_2 =
(from c in _ctx.ContactOperationPlaces
where query.Where(x=> x.Name == c.Contact.Name).Count() > 0
select c);
//Now query_2 contains all contactoperationsplaces which have a contact that was found in var query
Conversely, there is a much easier way to do this, and that's by skipping the first part entirely.
IQueryable<ContactOperationPlaces> query_2 =
(from c in _ctx.ContactOperationPlaces
where c.Contact.Name.ToLower().Contains(strName.ToLower())
select c);
If you're using Entity Framework, you don't have to do any joins as long as you defined associations between the tables.
Now that I look at it, my second solution is far more efficient and easier. But if you need to do some other processing inbetween these commands, solution one works too :)
If you need more explanation, feel free to ask :)

Making a LINQ query better

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
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().
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).
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);
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();

Categories

Resources