block access by query result - c#

I have Hospitals and Medical Specialities.
My Medical Specialities page return data by hospital ID in this way:
localhost/MedicalSpecialities/1, 1 is the HospitalID. if I change manually the link I can access any hospital info.
I have users associated to hospitals in this way:
I need to query the Hospital ID's that user have associated AND check if the current HospitalID is on the list.
This return all hospitals that user have connected:
var userID = User.Identity.GetUserId();
var result = db.Hospitals.Include("UserHospitals")
.Where(x => x.UserHospitals
.Any(u => u.Id == userID))
.ToList();

You can basically update the condition in your Any() method to include a check against the HospitalId column.
var hospitalId =5;
var result = db.Hospitals
.Include(y=>y.UserHospitals)
.Where(x => x.UserHospitals.Any(u => u.Id == userID
&& u.HospitalID==hospitalId ))
.ToList();
If you are expecting only a single hospital for this condition, you may also consider using FirstOrDefault() method.
var singleHospital = db.Hospitals
.Include(y=>y.UserHospitals)
.Where(x => x.UserHospitals.Any(u => u.Id == userID
&& u.HospitalID==hospitalId ))
.FirstOrDefault();
if(singleHospital!=null)
{
//Safely use it.
}

Related

How to select data only with a unique email field?

I need to select data from a table. The data has an Email field which is not unique. Now i only want to select the first item with an Email of its kind and ignore the others.
I tried:
var users = _context.Appointments.Where(p => (p.userId == userId))
.Select(p => new MyUserModel
{
Id = p.Id,
Email = p.User.Email
});
return users.ToList();
I wanted to use Distinct(), but my elements are not unique, they have a different id.
How could I go about doing that?
However, if this field is repeated, then I do not need to select this
element
You can group by the email and then perform a filter to retain the objects that don't repeat by email and then follow it with your current logic. Example:
var users = _context.Appointments
.Where(p => p.userId == userId)
.GroupBy(p => p.User.Email)
.Where(g => g.Count() == 1) // if there is only one object with this email, then retain it otherwise discard it
.Select(g => g.First())
.Select(p => new MyUserModel
{
Id = p.Id,
...
Email = p.User.Email
...
});
return users.ToList();

Linq nested or inner query

Suppose I have a list of employees and each employee has several projects. I can get a given employee using:
var employee = employees.SingleOrDefault(x => x.Id == "id");
But how can I filter also project for the employee?
For example:
var employee = list
.SingleOrDefault(x => x.Key == employeeKey &&
x.Projects.SingleOrDefault(p => p.Key == projectKey));
If you want to filter down the Projects after getting the Employee you can use a .Select().
var result = employees.Where(e => e.Id == id).Select(e => new Employee
{
Id = e.Id,
Projects = e.Projects.SingleOrDefault(p => p.Key == projectKey)
}).SingleOrDefault();
So you can get the data you need in one step, but you have to assign the properties by yourself.
Another way is to first get your Employee and then filter down the projects, like BoredomOverload suggested:
var employee = employees.SingleOrDefault(x => x.Id== "id");
employee.Projects = employee.Projects.SingleOrDefault(p => p.Key == projectKey);
Either way you get the employee and the Projects of that Employee filtered.
var employee = employees.SingleOrDefault(
x => x.Id.Equals("id") && x.project.Equals("project")
);
Use Any() LINQ method like
var employee = employees.SingleOrDefault(x => x.Id== "id" && x.Projects.Any(p => p.Id == "Id"));
Moreover, You are filtering based on employee ID x.Id== "id" and mostly that employee ID would a primary key (Unique in nature) and in such case filtering just by Id would be much enough I believe
SingleOrDefault returns the object if found or null. So, in your case, it returns all employees because you are not testing anything. You just said if the project is there then return it.
Use Any instead which will return a boolean value if exist or not:
var employee = list.SingleOrDefault(x => x.Key == customerKey && x.Projects.Any(p => p.Key == projectKey));
If you need to filter if he has only one project with the specific key:
var employee = list.SingleOrDefault(x => x.Key == customerKey && x.Projects.Count(p => p.Key == projectKey) == 1);
You can also achieve it with SingleOrDefault but test the value with null:
var employee = list.SingleOrDefault(x => x.Key == customerKey && x.Projects.SingleOrDefault(p => p.Key == projectKey) != null);
If you want the return type to be more specific then use the select.
If it didn't work, try to add "include" to the list:
list.Include("Projects").... the rest of the query

How I can optimize my LINQ query?

I have written following LINQ query to return list of Groups and then iterating list separately.
I only need list of Groups of User with specific email.
I am sure this might not be the right away.
Can i re-write it in a better way (performance-wise) ?
var groups = _context.Users.Where(m => m.Email == email)
.Include(g => g.Customer)
.ThenInclude(r => r.CustomerGroups)
.ThenInclude(t => t.Group)
.First().Customer.CustomerGroups;
foreach (var group in groups)
{
var s = group.Group;
//do something
}
If you need just CustomerGroup entities with the related Group entity (as I interpret your clarification in the comment section) it's inefficient to fetch other related entities (User and Customer). You can make EF to fetch only the entities you are interested in like this:
var groups =
(
from user in _context.Users
from customerGroup in user.Customer.CustomerGroups
where user.Email == email
select customerGroup
).Include(cg => cg.Group);
Or when CustomerGroup stores no relevant data, just relationships:
var groups =
(
from user in _context.Users
from customerGroup in user.Customer.CustomerGroups
where user.Email == email
select customerGroup.Group
);
Try this :
That is make query on CustomerGroups table so You don't need Include Customer and CustomerGroups .
var customerGroups = _context.CustomerGroups.Where(m => m.Customer.User.Email == email)
.Include(t => t.Group).
Select(s=> new CustomerGroupModel {
A= s.A,
B= s.B,
…
Group = s.Group
}).ToList();
Or
var customerGroups = _context.Customer.Where(m => m.User.Email == email)
.Include(r => r.CustomerGroups).ThenInclude(t => t.Group).
Select(s=> new CustomerGroupModel {
A= s.CustomerGroups.A,
B= s.CustomerGroups.B,
…
Group = s.CustomerGroups.Group
}).ToList();

removing items from a list based on two properties

I have the a list called orderList of type Order which has two properties. The list looks something like below.
Id Status
123 Good
878 Good
432 Good
123 Void
What I would like to do is to remove any orders that have a Status which is void and any Good orders which have the same Id as a void order. So the result would give me,
Id Status
878 Good
432 Good
What is the best way to do this? Is it just getting a list of void orders using linq and then looping through this new list to remove Good orders which share the same Id?
You can group and filter later, and flatten the groups at the end:
var result= list.GroupBy(e=>e.Id)
.Where(g=>g.Any(r=>r.Status=="Good") && g.All(r=>r.Status!="Void"))
.SelectMany(g=>g);
So you need to group first by your ID, after that specified your data and create new Field Remove which should be true if you have any Status Void in your group status collection. After that take objects only where the Remove is false and in the end create your RootOrder.
var result = list.GroupBy(x => x.ID)
.Select(x => new { ID = x.Key, Status = x.FirstOrDefault().Status, Remove = x.Any(y => y.Status == "Void") })
.Where(g => g.Remove == false)
.Select(r => new RootOrder { ID = r.ID, Status = r.Status }).ToList();
Full code example: dotNetFiddle
One way would be grouping, filtering and flattening the remaining groups - If you want a more 'literal' query you can try the following:
First filter out all of the non-Good Orders using Where:
orderList = orderList.Where(x => x.Status == Status.Good)
Then filter out all of the remaining Orders for which there are non-Good Orders in orderList containing the same Id using Where and Any:
.Where(x => !orderList
.Any(y => y.Status == Status.Void && y.Id == x.Id)
Finally, use the ToList() to take the returned IEnumerable as a List:
orderList = orderList
.Where(x => x.Status == Status.Good)
.Where(x => !orderList
.Any(y => y.Status == Status.Void && y.Id == x.Id)
.ToList();

How can I restrict a LINQ Query?

What I would like to do is to find the first or default value of the role Name given the Id.
Here is the LINQ I tried:
var roleId = await db.AspNetRoles
.Select(r => r.Id)
.Where(r => r.)
.FirstOrDefault();
Here is my class:
I have this class in Entity Framework Asp.Net Identity
public AspNetRole()
{
this.AspNetUsers = new List<AspNetUser>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<AspNetUser> AspNetUsers { get; set; }
When I look at what options I have after the r. I find that I cannot put in a name like r.Name
Can someone show me what I am doing wrong?
Because the Select projects the source onto a new mapping, in this case only containing the int Id. From there on you'll be operating on an IEnumerable<int>, which don't have a Name property.
You could use:
.Select(r => new { r.Id, r.Name })
To project into an anonymous object only containing Id and Name.
Alternatively you can query first, then project:
await db.AspNetRoles
.Where(r => r.Id == someId)
.Select(r => r.Name)
.FirstOrDefault();
Or omit the projection (the .Select() call) entirely, but it all depends on what you want to do with the results.
You have to Change the order of the execution to
var roleId = await db.AspNetRoles
.Where(r => r.Name = "your Name")
.Select(r => r.Id)
.FirstOrDefault();
The Problem in your code is that you Select the Id which is of type int. Then when you call Where you have a list of ints available. If you swap where and select you first filter on AspNetRoles then select the int.
Your issue makes perfect sense:). The problem is you are projecting before filtering. After the select you'll get a collection of ints. What you need to do is revers the query like so :
var roleId = await db.AspNetRoles
.Where(r => r.Name=%smething%)
.Select(r => r.Id)
.FirstOrDefault();
I hope this helps :)
Try this:-
var roleId = await db.AspNetRoles
.Where(r => r.Id == YourId)
.Select(x => x.Name)
.FirstOrDefault();
Or (If you are sure You have that Id):-
var roleId = await db.AspNetRoles.FirstOrDefault(x => x.Id == YourId).Name;
But, Please note it will throw Null Reference Exception if no id matches :)
Just change the order like so:
var roleId = await db.AspNetRoles
.Where(r => r.Id == 24)
.Select(s => s.Name)
.FirstOrDefault():
You select r.Id in your select so the result only contains the Ids. You could also do it like this:
var role = await db.AspNetRoles.FirstOrDefault(r => r.Name == "Some name");
if (role != null)
{
// more code about role.Id
}

Categories

Resources