Where clause on collection - c#

I'm using the BAGA code from Julie Lerman's DbContext book. I want to recreate the following SQL query in LINQ and put the results in a List collections and am having problems.
http://learnentityframework.com/downloads/
SELECT * FROM baga.Locations d
LEFT JOIN Lodgings l ON d.LocationID = l.destination_id
WHERE d.Country = 'usa'
AND (l.MilesFromNearestAirport > 5 or l.MilesFromNearestAirport is null)
So, in English, get all locations (destinations) that are in the USA and include all the related lodgings where MilesFromNearestAirport > 5
The syntax doesn't compile but I was hoping for something similar to below
var dests = context.Destinations
.Where(d => d.Country == "USA" && d.Lodgings.Where(l => l.MilesFromNearestAirport > 5))
.Select(d => d)
.ToList();
Any ideas?

As #Sampath says, this is normally done with navigation properties but I believe that his code does not exactly do what you want. This (I think) is closer:
var dests = context.Destinations
.Where(d => d.Country == "USA")
.Select(d =>
new { d,
RemoteLodgings = d.Lodgings
.Where(l => l.MilesFromNearestAirport > 5)}
.ToList();
But it still does not do what you want if I take your requirement to the letter. You want the location with the lodgings included, i.e. the navigation properties loaded partly. This can be convenient when the entities are to be serialized and sent to a (web) client in one package. (Although the method above would be OK for that too).
But it is possible to make a collection with partly loaded navigation properties.
The book, at page 40, shows how you can partly load navigation properties of a single entity (in short: context.Entry(entity).Collection(e => e.Children).Query().Where(condition). But as said, that's only one instance. It's not the best method to do that for a collection of entities.
The funny thing is (as a colleague of mine found out), you can easily do it for a collection of entities by loading the required collection elements into the context separately:
var lodgings = context.Lodgings
.Where(l => l.MilesFromNearestAirport > 5
&& l.Destination.Country == "USA")
.ToList();
Now if you loop through context.Destinations.Where(d => d.Country == "USA") you will see that their lodgings are loaded with the ones ">5". Probably because at this point EF executed relationship fixup. (Lazy loading disabled, because lazy loading will fully load the navigation properties).
Edit (after your comment)
I couldn't agree more when you say it is a bit of a hack. Actually, I forgot to mention that in the first place. The problem is that the whole mechanism collapses when lazy loading happens to be activated by someone who's not aware of the what the code is for. I don't like code that depends on the state in a way that is not obvious. So I would always prefer the first approach.

Normally this is done using navigation properties which are loaded when you get the entity.
However you can also do this with the following:
(from d in baga.Locations
from l in Lodgings
where (d.LocationID == l.destination_id)
where (d.Country = 'usa' && (l.MilesFromNearestAirport > 5 || l.MilesFromNearestAirport == null))
select d)
.ToList();
I hope this will help to you.

How about using the LINQ join?
var res = from d in context.Destinations
join l in context.Lodgings on d.LocationID equals l.destination_id
where (l.MilesFromNearestAirport > 5 || l.MilesFromNearestAirport == null)
&& d.Country = "usa"
select new {
Destination = d,
Location = l
}

Related

Multiple conditions in joins with Linq query

How to apply Multiple conditions in joins and in operator in linq query. I tried to implement the below code and got strucked. Kindly let me know to implement.
Query:
SELECT now() as "time", COALESCE (sum(inv.total_invoice_amount),0) as value1, loc.location_name as metric FROM location loc
LEFT JOIN location_user_map LUM ON LUM.location_id = loc.location_id
LEFT OUTER JOIN invoice inv on inv.client_id IN($client_ids) AND inv.location_id = loc.location_id AND $__timeFilter(inv.end_time)
AND inv.status IN (SELECT status_id FROM status WHERE status IN('Paid','Partialy Paid','Open', 'Completed'))
WHERE loc.client_id IN($client_ids) AND loc.location_name NOT IN('Local Purchase') AND loc.location_id != 0 AND LUM.user_id IN($user_ids)
AND inv.is_active = true
GROUP BY loc.location_name
ORDER BY value1 desc
Code:
using (TransactionContext oTransactionContext = new TransactionContext(iClientID, true))
{
var oPT_Det = (from loc in oTransactionContext.Location
join lum in oTransactionContext.LocationUserMap on loc.LocationId equals lum.LocationId
join inv in oTransactionContext.Invoice on new { loc.LocationId } equals new { inv.LocationId }
select loc);
return oPT_Det;
}
It's not supposed to be this hard, by the way; part of the magic of EF is that it knows how your entities link together and it will do the joins for you. The idea is to use it in the necessary ways for it to write the SQL for you, rather than bring your SQL into C#, jiggle the order and syntax a bit:
var statusIds = context.Statuses
.Where(s => new[]{"Paid","Partialy Paid","Open", "Completed"}.Contains(s.Status))
.Select(s => s.StatusId)
.ToArray();
context.Locations
.Where(l =>
clientIdsArray.Contains(l.ClientId) &&
l.Name != "Local Purchase" &&
l.LocationId != 0 &&
userIdsArray.Contains(l.LocationUserMap)
)
.Select(l => new {
l.LocationId,
l.Invoices.Where(i =>
clientIdsArray.Contains(i.ClientId) &&
statusIds.Contains(I.StatusId)
).Sum(i.TotalInvoiceAmount)
});
Or, perhaps you would start from Invoices instead of locations. It can be easier to start from a many end because the navigation to a one-end is a single property rather than a collection:
context.Invoices.Where(i =>
i.LocationId != 0 &&
i.Location.Name != "Local Purchse" &&
clientIdsArray.Contains(i.Location.ClientId) &&
statusIds.Contains(i.StatusId) &&
i.Location.UserMaps.Any(um => userMapIds.Contains(um.UserId))
)
.GroupBy(i => i.Location.Name)
.Select(g => new { Name = g.Key, Tot = g.Sum(i => i.TotalInvoiceAmount))
EF strives to allow you to just manipulate the entity graph as though it were a local thing, and it manages the DB side for you. Sure, sometimes you have to structure things in a certain way to get the results you want or to get it to craft an SQL in a particular way but..
Note that I don't guarantee these queries as written here solve your problem, or even work/compile; there's a relative lack of info on your question and I've made some assumptions (declared) about your relationships. The purpose of this answer is to point out that you can/are supposed to "leave the SQL at the door" when you come into using EF, rather than thinking of everything in SQL terms still and bending your C# approach to SQL ways. It's intended to be rare that we write the word "join" when working with EF

Linq nested loop search one specific top level element , collection within collections

I want to get the company whose employees id card issued with the specific number, sort of finding the exact element inside nested collection.
Using first or default 3 times does not seems to be a correct way.
> var company = cprIdentificationReply.Companies
> .FirstOrDefault(x => (x.Employee
> .FirstOrDefault(y => (y.IDCardIssued
> .FirstOrDefault(z => z.CardNumber
> .Equals(number,StringComparison.InvariantCultureIgnoreCase))) != null)
> != null));
What can be a proper way of achieving the same?
You may want to use the Any extension method:
var companies = cprIdentificationReply.Companies
.Where(x => (x.Employee
.Any(y => (y.IDCardIssued
.Any(z => z.CardNumber
.Equals(number, StringComparison.InvariantCultureIgnoreCase)
)
)
).ToList();
If you want better looking code why don't use LINQ query syntax.
I always find it easier to read LINQ query when looking at someone else's code, especially for complex operations.
Something like this:
var company = (from company in cprIdentificationReply.Companies
from empl in company.Employee
from idCardIss in empl .IDCardIssued
where idCardIss.CardNumber.Equals(number, StringComparison.InvariantCultureIgnoreCase)
select c).FirstOrDefault();

Filtering Related Entites with Entity Framework

According to this StackOverflow answer:
Linq to Entities - how to filter on child entities
you should be able to filter down the list of related entities in Entity Framework by utilizing a projection, like I've done here:
Company company = _context.Company
.Where(g => g.CompanyId == id)
.Select(comp => new
{
group = comp,
operators = comp.Operator,
formFamilies = comp.FormFamily.Where(ff => ff.IsActive ?? false)
}).AsEnumerable().Select(i => i.group).FirstOrDefault();
To give a quick overview of what I'm trying to obtain here, I'm trying to get a list of all of the active form families associated with this company object, however, whenever I restrict the results in any way, the result set is empty.
If the line were formFamilies = comp.FormFamily then it returns two results, one active one inactive
If the line is formFamilies = comp.FormFamily.Where(ff => true) then it returns nothing
If the line is formFamilies = comp.FormFamily.OrderBy(ff => ff.FormFamilyId) then it returns nothing.
Any sort of modification that I do to comp.FormFamily means the result set returns nothing, I've dug through the deepest sections of SA to try to find a solution, and tried every solution I've found, but nothing seems to cause this list to return anything.
Assuming that Company and FormFamily entities has one to many relationship I would suggest to use a join statement.Something like this should give you what you are looking for.
var company = from c in _context.Company
join f in _context.FormFamily
on c.Id equals f.CompanyId
where c.Id == id
select new Company()
{
Id = c.Id,
operators = c.Operator.ToList(),
formFamilies = c.FormFamily.Where(x=>x.IsActive ==
false).ToList()
} .FirstOrDefault();
Hope this helps.
I didn't quite understand what is your query is supposed to do. But it seems to me that you cannot just call Select method on another Select result method.
Anyway, you could simply use Include methods instead of projecting.
var company = _context.Company
.Where(c => c.Id == id)
.Include(c => c.FormFamily).Where(ff => ff.IsActive ?? false)
.ToList();
Did not test it. To prove it works or not be sure put an entity model in the question. Then I may produce more accurate answer.

Entity Framework Query is too slow

I have to put a complex query on your database. But the query ends at 8000 ms. Do I do something wrong? I use .net 1.1 and Entity Framework core 1.1.2 version.
var fol = _context.UserRelations
.Where(u => u.FollowerId == id && u.State == true)
.Select(p => p.FollowingId)
.ToArray();
var Votes = await _context.Votes
.OrderByDescending(c => c.CreationDate)
.Skip(pageSize * pageIndex)
.Take(pageSize)
.Where(fo => fol.Contains(fo.UserId))
.Select(vote => new
{
Id = vote.Id,
VoteQuestions = vote.VoteQuestions,
VoteImages = _context.VoteMedias.Where(m => m.VoteId == vote.Id)
.Select(k => k.MediaUrl.ToString()),
Options = _context.VoteOptions.Where(m => m.VoteId == vote.Id).Select( ques => new
{
OptionsID = ques.Id,
OptionsName = ques.VoteOption,
OptionsCount = ques.VoteRating.Count(cout => cout.VoteOptionsId == ques.Id),
}),
User = _context.Users.Where(u => u.Id == vote.UserId).Select(usr => new
{
Id = usr.Id,
Name = usr.UserProperties.Where(o => o.UserId == vote.UserId).Select(l => l.Name.ToString())
.First(),
Surname = usr.UserProperties.Where(o => o.UserId == vote.UserId)
.Select(l => l.SurName.ToString()).First(),
ProfileImage = usr.UserProfileImages.Where(h => h.UserId == vote.UserId && h.State == true)
.Select(n => n.ImageUrl.ToString()).First()
}),
NextPage = nextPage
}).ToListAsync();
Have a look at the SQL queries you generate to the server (and results of this queries). For SQL Server the best option is SQL Server Profiler, there are ways for other servers too.
you create two queries. First creates fol array and then you pass it into the second query using Contains. Do you know how this works? You probably generate query with as many parameters as many items you have in the array. It is neither pretty or efficient. It is not necessary here, merge it into the main query and you would have only one parameter.
you do paginating before filtering, is this really the way it should work? Also have a look at other ways of paginating based on filtering by ids rather than simple skipping.
you do too much side queries in one query. When you query three sublists of 100 items each, you do not get 300 rows. To get it in one query you create join and get actually 100*100*100 = 1000000 rows. Unless you are sure the frameworks can split it into multiple queries (probably can not), you should query the sublists in separate queries. This would be probably the main performance problem you have.
please use singular to name tables, not plural
for performance analysis, indexes structure and execution plan are vital information and you can not really say much without them
As noted in the comments, you are potentially executing 100, 1000 or 10000 queries. For every Vote in your database that matches the first result you do 3 other queries.
For 1000 votes which result from the first query you need to do 3000 other queries to fetch the data. That's insane!
You have to use EF Cores eager loading feature to fetch this data with very few queries. If your models are designed well with relations and navigation properties its easy.
When you load flat models without a projection (using .Select), you have to use .Include to tell EF Which other related entities it should load.
// Assuming your navigation property is called VoteMedia
await _context.Votes.
.Include(vote => vote.VoteMedia)
...
This would load all VoteMedia objects with the vote. So extra query to get them is not necessary.
But if you use projects, the .Include calls are not necessary (in fact they are even ignored, when you reference navigation properties in the projection).
// Assuming your navigation property is called VoteMedia
await _context.Votes.
.Include(vote => vote.VoteMedia)
...
.Select( vote => new
{
Id = vote.Id,
VoteQuestions = vote.VoteQuestions,
// here you reference to VoteMedia from your Model
// EF Core recognize that and will load VoteMedia too.
//
// When using _context.VoteMedias.Where(...), EF won't do that
// because you directly call into the context
VoteImages = vote.VoteMedias.Where(m => m.VoteId == vote.Id)
.Select(k => k.MediaUrl.ToString()),
// Same here
Options = vote.VoteOptions.Where(m => m.VoteId == vote.Id).Select( ques => ... );
}

C# Linq Union Returning Null

I have a simple linq query that unionizes two tables that implement the same interface.
For this example, lets say IAnimal.
var q = (from d in db.Dogs
where d.AnimalID = PetID
select d.Name)
.Union
(from c in db.Cats
where c.AnimalID = PetID
select c.Name)
So if the dog portion has no members, q gets assigned {"", {whatever is in cat}}. Is there a way to remove that empty record without doing .Where(x=>x!="" | x!= String.Empty) after the query?
I know its not that big of a deal, but it seems like there should be a better way?
How is that not a good way? What is wrong with it?
Well.. there is one thing wrong with it. If should be:
.Where(x => !string.IsNullOrEmpty(x))
If you are using Entity Framework or some other LINQ provider which can't handle IsNullOrEMpty then the code should be:
.Where(x => x != null && x != "")
Your code using | instead of && and checks against the empty string twice but never null.

Categories