Selecting parents of children who meet certain criteria - c#

So, I have retrieve a bunch of Countries from my database:
var countries = dbContext.Countries.toList();
Then, depending on program flow, I filter further on these Contries:
var asianContries = result.where(c=>c.continent == "asia").toList();
My Countries table is a "parent" table to a Cities table, with each city relating to a country. The cities table contains population info, which I further want to filter on.
I want, from the already filtered "asianCountries" list object, a list of countries in which there are cities with more than 500,000 people. I just went stone-cold in trying to figure out how to do it. Also, I'm new to this stuff.
Why this multistep-filtering instead of selecting on all criteria in one go? Complexity of program flow. Long story. :-)

If i understood correctly you have filtered to Asian countries already now you want to filter those results further.
I would do it one of two ways either if you have the population as int
var cities = asianCountries.Select(x => x.cities.Where( y => y.population > 500000)).ToList();
if it is a string then
var cities = asianCountries.Select(x => x.cities.Where(y => Convert.ToInt32(y.population) > 500000)).ToList();
This should work i think.

Example for joining multiple tables ,
from ct in dbContext.Countries
join ci in dbContext.Cities on ct.CityID equals ci.ID
where (ct.continent == "asia") && (ci.Population == // yourCondition)
select new { country = ct.Name, city = ci.Name , // other fields you want to select
};
You could take a reference how to join multiple tables here

If an object implements IQueryable<T> the query is only executed when the object is enumerated. This means that you can chain queries together and execution will be deferred until you call, for example ToList().
In your example you could do something like:
// to select the cities
var largeCities = dbContext.Countries
.Include(t => t.Cities)
.Where(c=> c.continent == "asia"
&& c.Cities.Population > 500000)
.Select(c => c.Cities).ToList();
// EDIT
// to select the countries that have these cities
var countries = dbContext.Countries
.Include(t => t.Cities)
.Where(c=> c.continent == "asia"
&& c.Cities.Population > 500000)
.ToList(); // remove .Select(c => C.Cities) if you want the countries
Or
var largeCities = asianCountries
.Where(c => c.Cities.Population > 500000)
.Select(c => c.Cities)
.ToList();

Related

The appropriate way to query List from different variable?

I have a table in which display many-to-many relationship. From the manager user, I will get a list of location that under manager management. Then from that list, I will get a list of user that related to those location. Here's my first implement to get location list
var locationManager = db.UserLocation.Where(x => x.userID == userID).ToList();
I don't know the appropriate to do the next step. Let's say manager's id is the userID with number 2. Therefor, the first line of code will return me with a list of UserLocation with userID = 2 which contain total of two locations with id of 1 and 2.
Database table Image
In my mind, the next line of code to print out UserLocation would be like this but I don't want to hard code number 1 or 2.
var locationRelated = db.UserLocation.Where(x => x.locationId == 1 || x.locationId == 2).ToList();
You can use Enumerable.Contains to filter with collection:
var locationIds = new []{1, 2};// get ids somehow
var locationRelated = db.UserLocation
.Where(x => locationIds.Contains(x.locationId))
.ToList();
But it seems that there is typo, case you are querying db.UserLocation again. If you want to get Location(assume it is called like that), you can do that in one query, if your relations in enities are set up correctly:
db.UserLocation
.Where(x => x.userID == userID)
.Select(x => x.Location)
.ToList();

How to sort something in LINQ based on many dates?

Hello this is a LINQ Query but it doesn't sort properly because four different dates are involved.
var EventReportRemarks = (from i in _context.pm_main_repz
.Include(a => a.PM_Evt_Cat)
.Include(b => b.department)
.Include(c => c.employees)
.Include(d => d.provncs)
where i.department.DepartmentName == "Finance"
orderby i.English_seen_by_executive_on descending
orderby i.Brief_seen_by_executive_on descending
orderby i.French_seen_by_executive_on descending
orderby i.Russian_seen_by_executive_on descending
select i).ToList();
All i want is that it should somehow combine the four dates and sort them in group not one by one.
For Example, at the moment it sorts all English Reports based on the date that executive has seen it, then Brief Report and So on.
But i want that it should check which one is seen first and so on. For example if the first report which is seen is French, then Brief, then English then Russian, so it should sort it accordingly.
Is it Possible??
You need to have them all in one column. The approach I would do, assuming that the value of the respective cells is null, when you don't want them to show up in the order by:
var EventReportRemarks = (from i in _context.pm_main_repz
.Include(a => a.PM_Evt_Cat)
.Include(b => b.department)
.Include(c => c.employees)
.Include(d => d.provncs)
where i.department.DepartmentName == "Finance"
select new
{
Date =
(
i.English_seen_by_executive_on != null ? i.English_seen_by_executive_on :
i.Brief_seen_by_executive_on != null ? i.Brief_seen_by_executive_on :
i.French_seen_by_executive_on != null ? i.French_seen_by_executive_on :
i.Russian_seen_by_executive_on
)
}).ToList().OrderBy(a => a.Date);
In the select clause you could add more columns if you whish.
Reference taken from here.
Why not just use .Min() or .Max() on the dates and then .OrderBy() or .OrderByDescending() based on that?
Logic is creating a new Enumerable (here, an array) with the 4 dates for the current line, and calculate the Max/Min of the 4 dates: this results in getting the latest/earliest of the 4. Then order the records based on this value.
var EventReportRemarks = (from i in _context.pm_main_repz
.Include(a => a.PM_Evt_Cat)
.Include(b => b.department)
.Include(c => c.employees)
.Include(d => d.provncs)
where i.department.DepartmentName == "Finance"
select i)
.OrderBy(i => new[]{
i.English_seen_by_executive_on,
i.Brief_seen_by_executive_on,
i.French_seen_by_executive_on,
i.Russian_seen_by_executive_on
}.Max())
.ToList();
Your problem is not a problem if you use method syntax for your LINQ query instead of query syntax.
var EventReportRemarks = _context.pm_main_repz
.Where(rep => rep.Department.DepartmentName == "Finance")
.OrderByDescending(rep => rep.English_seen_by_executive_on)
.ThenByDescending(rep => rep.Brief_seen_by_executive_on)
.ThenByDescending(rep => rep.French_seen_by_executive_on descending)
.ThenByDescending(rep => resp.Russian_seen_by_executive_on descending)
.Select(rep => ...);
Optimization
One of the slower parts of a database query is the transport of selected data from the DBMS to your local process. Hence it is wise to limit the transported data to values you actually plan to use.
You transport way more data than you need to.
For example. Every pm_main_repz (my, you do love to use easy identifiers for your items, don't you?), every pm_main_repz has zero or more Employees. Every Employees belongs to exactly one pm_main_repz using a foreign key like pm_main_repzId.
If you use include to transport pm_main_repz 4 with his 1000 Employees every Employee will have a pm_main_repzId with value 4. You'll transport this value 1001 times, while 1 time would have been enough
Always use Select to select data from the database and Select only the properties you actually plan to use. Only use Include if you plan to update the fetched objects
Consider using a proper Select where you only select the items that you actually plan to use:
.Select(rep => new
{
// only Select the rep properties you actually plan to use:
Id = rep.Id,
Name = rep.Name,
...
Employees = rep.Employees.Select(employee => new
{
// again: select only the properties you plan to use
Id = employee.Id,
Name = employee.Name,
// not needed: foreign key to pm_main_repz
// pm_main_repzId = rep.pm_main_repzId,
})
.ToList(),
Department = new
{
Id = rep.Department,
...
}
// etc for pm_evt_cat and provencs
});

How to modify the condition for a LINQ request to remove users that are in another table?

I have a LINQ request that get data about the users that are present in the Appointments table
var patients = _context.Users.Where(x => x.Appointments.Any(a => a.DoctorId == doctorId))
Now I need to modify this condition, not to take those users who fell into the black list. Now I need to modify this condition, not to take those users who fell into the black list. In another place, I take them as follows:
var banned = _context.DoctorBlackLists.Where(x => x.DoctorId == doctorId)
How I'm in one query to combine these conditionÑ–, get the users that are in the table Appointments, except those that are present in the table DoctorBlackLists?
Both tables, Appointments and DoctorBlackLists, have a column DoctorId and UserId, its associated with the Users table
Well, you could create a list of banned user ids:
var bannedUserIds = _context.DoctorBlackLists
.Where(x => x.DoctorId == doctorId)
.Select(x => x.UserId)
.ToList();
And use it (check that the patient is not in a banned list):
var patients = _context.Users.Where(x =>
!bannedUserIds.Contains(x.Id)
&& x.Appointments.Any(a => a.DoctorId == doctorId))
.ToList();

join 3 tables in linq getting all entries from two tables

I have 3 tables
A project table
A product table
An update table
The product table holds different products from a project, and the update table holds updates made to various products and holds a reference to the user who did it.
Basically what I want is to have a query that returns all products (since products to projects is a many to one relation) ordered by the date they we're last updated by the user who is currently logged in.
This is my current query:
IEnumerable<ProjectProduct> list =
from joined in
(from product in db.GetTable<Product>()
join project in db.GetTable<Project>()
on product.ProjectId equals project.ID
select new { product, project })
join projectupd in db.GetTable<ProjectUpdate>()
on joined.product.ID equals projectupd.ProductID
where projectupd.CreatedBy == ParamUser
orderby projectupd.LastUpdate
select new ProjectProduct(joined.project, joined.product);
However, the result I'm getting is only the entries in the update table, and not all the existing products. I know that the "where" clause makes it only select the updates created by a specific user, so I'm on the right track, but I have tried a couple of things to make the query successful, without luck though.
Does anybody have a suggestion on how to get the desired result?
Here's an answer that's a little verbose, and it uses method-chain syntax, but I do think it does what your looking for:
var products = db.GetTable<Product>();
var projects = db.GetTable<Project>();
var projectUpdates = db.GetTable<ProjectUpdate>();
var latestProjectUpdatesForUser = projectUpdates
.Where(x => x.CreatedBy == paramUser)
.GroupBy(x => x.ProductId)
.Select(g => g.OrderByDescending(x => x.LastUpdate).First());
var list = products
.Join(
projects,
product => product.ProjectId,
project => project.Id,
(product, project) => new
{
Product = product,
Project = project,
Update = latestProjectUpdatesForUser.FirstOrDefault(u => u.ProductId == product.Id)
}
)
.OrderByDescending(x => x.Update != null ? (DateTime?)x.Update.LastUpdate : null)
.ThenBy(x => x.Project.Id)
.ThenBy(x => x.Product.Id)
.Select(x => new ProjectProduct { Project = x.Project, Product = x.Product});
It takes advantage of the fact that DateTime? is sortable and that null values end up last when using OrderByDescending.

LINQ - SELECT DISTINCT with NOT IN

I'm have a SQL statement which I am trying to transform in a LINQ statement...
SELECT DISTINCT mc.*
FROM ManufractorCategories mc
WHERE mc.Active = 'true'
AND mc.Folder = 'false'
AND (mc.Id not in (SELECT Category_id FROM Manufractor_Category
WHERE Manufractor_id = 3));
That's my last, not working LINQ statement
(IQueryable<object>)db.ManufractorCategories
.Where(o => o.Active == active)
.Where(o => o.Folder == folder)
.Select(i => new { i.Id, i.Folder }).Except(db.Manufractor_Categories.Where(t => t.Manufractor_id == id).Select(t => new { t.Category_id })).Distinct();
I've tried the whole Sunday on that, but the Except statement won't work.
Thanks in advances for any help!
The Except method requires two sets of the same type - this means that you would have to select objects of type ManufractorCategory in the nested query as well as in the outer query - then it would select all categories that are in the first one and not in the second one.
An easier alternative is to use the Contains method to check whether the current ID is in a list of IDs that you want to filter. The following should work:
var q =
db.ManufractorCategories
.Where(o => o.Active == active)
.Where(o => o.Folder == folder)
.Select(i => new { i.Id, i.Folder })
.Where(o =>
!db.Manufractor_Categories
.Select(t => t.Manufractor_id)
.Contains(o.Id)
.Distinct();
And a simplified version using query syntax:
var q =
from o in db.ManufractorCategories
where o.Active == active && o.Folder == folder &&
db.Manufractor_Categories
.Select(t => t.Manufractor_id)
.Contains(o.Id)
select new { i.Id, i.Folder };
The Except statement is going to get a list of objects with the Category_id property. However, you're query has a result that contains objects with the Id and Folder properties. The query will most likely be unable to see where these objects are equal, and so, the Except clause won't take effect.

Categories