The appropriate way to query List from different variable? - c#

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

Related

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
});

Selecting parents of children who meet certain criteria

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

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

InvalidOperationException: Sequence contains more than one element

I have the following code below for a payroll program.
The first dictionary holds the employee IDs and corresponding basic pays held in a master data table.
The second dictionary holds the employee IDs and corresponding basic pays held in a salary fitment table - used for processing.
I want to update the salary fitment basic pays for each employee ID that do not match in the master table. (Changes in salary).
var OHEMDictionary = employees.OrderBy(es => es.empID)
.ToDictionary(od => od.empID,
od => od.salary);
var SalaryFitmentDictionary = salaryFitments
.Where(x => x.U_PD_Code.Trim().ToString() == "SYS001")
.OrderBy(es => es.U_Employee_ID)
.ToDictionary(od => od.U_Employee_ID,
od => od.U_PD_Amount);
var difference = OHEMDictionary
.Where(kv => SalaryFitmentDictionary[kv.Key] != kv.Value);
difference.ToList().ForEach(x =>
{
decimal salary = x.Value.Value;
var codeToUpdate = salaryFitments
.Where(y => y.U_Employee_ID.Equals(x.Key))
.Select(z => z.Code)
.SingleOrDefault(); `**<---exception thrown here**`
var salaryFitment = salaryFitmentService
.GetSalaryFitment(codeToUpdate);
if (salaryFitment != null)
{
// Save record
salaryFitmentService
.UpdateSalaryFitment(salaryFitment, salary.ToString());
}
});
However, I keep getting the error 'Sequence contains more than one element'. How do I solve this error?
You can use FirstOrDefault() but SingleOrDefault throws an exception if more than one element exists.
Here you can see exactly what the single or default method does:
http://msdn.microsoft.com/en-us/library/system.linq.enumerable.singleordefault(v=vs.100).aspx

LINQ-To-SQL row number greater than

If i have an object of type Photo and a Result set which is sorted in a particular order, is there a way for me to get the position of the current Photo object in the result set. and then get all objects that would follow it?
You can do something like this (not terribly efficient):
var result =
photos.Select((p, i) => new { Index = i, Photo = p })
.SkipWhile(x => x.Photo != photo).Skip(1);
This will give you all photos following photo combined with their index in the original collection.
If you're sorting against an Id:
// gets the previous photo, or null if none:
var previousPhoto = db.Photos
.Where(p => p.Id < currentPhotoId)
.OrderByDescending(p => p.Id)
.FirstOrDefault();
// gets the next photo, or null if none:
var nextPhoto = db.Photos
.Where(p => p.Id > currentPhotoId)
.OrderBy(p => p.Id)
.FirstOrDefault();
If you have custom ordering, you'd need to replace the OrderBy/OrderByDescending expression with your custom ordering. You'd also need to use the same ordering criteria in Where() to get only those photos before or after the current photo.
Not sure if I understand correctly but this might help:
var result = photos.Skip(100); // 100 would be the position of current object.
// if you don't know the index of the current object:
// This won't work on LINQ to SQL directly, do it on a list or something.
var result = photos.SkipWhile(x => x != currentObject).Skip(1);
In reality, if you are dealing with a database, there should be some identifier (a set of columns) you are sorting by. If you want to do the whole thing on the server side, you can grab the properties of the current object and filter the result set specifically for objects that would come after that in the sort order you want.
Index of the photo:
result.IndexOf(photo);
Items after it:
result.SkipWhile((q, i) => i <= result.IndexOf(photo));

Categories