How can I restrict a LINQ Query? - c#

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
}

Related

LINQ efficiency

Consider the following LINQ statements:
var model = getModel();
// apptId is passed in, not the order, so get the related order id
var order = (model.getMyData
.Where(x => x.ApptId == apptId)
.Select(y => y.OrderId));
var orderId = 0;
var orderId = order.LastOrDefault();
// see if more than one appt is associated to the order
var apptOrders = (model.getMyData
.Where(x => x.OrderId == orderId)
.Select(y => new { y.OrderId, y.AppointmentsId }));
This code works as expected, but I could not help but think that there is a more efficient way to accomplish the goal ( one call to the db ).
Is there a way to combine the two LINQ statements above into one? For this question please assume I need to use LINQ.
You can use GroupBy method to group all orders by OrderId. After applying LastOrDefault and ToList will give you the same result which you get from above code.
Here is a sample code:
var apptOrders = model.getMyData
.Where(x => x.ApptId == apptId)
.GroupBy(s => s.OrderId)
.LastOrDefault().ToList();
Entity Framework can't translate LastOrDefault, but it can handle Contains with sub-queries, so lookup the OrderId as a query and filter the orders by that:
// apptId is passed in, not the order, so get the related order id
var orderId = model.getMyData
.Where(x => x.ApptId == apptId)
.Select(y => y.OrderId);
// see if more than one appt is associated to the order
var apptOrders = model.getMyData
.Where(a => orderId.Contains(a.OrderId))
.Select(a => a.ApptId);
It seems like this is all you need:
var apptOrders =
model
.getMyData
.Where(x => x.ApptId == apptId)
.Select(y => new { y.OrderId, y.AppointmentsId });

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

block access by query result

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.
}

Compare in Linq

I have a bunch of data in a database that i want to write a search function for. The problem is that i'm getting many duplicates.
The data is structured in Names and Surnames and i want to only send one unique of both so if i have two people with the first name Foo, and surname Bar only one will show.
No matter how I think of it I always come back to that I need to compare them.
var names = db.People
.Where(r => r.Name.Contains(q))
.OrderBy(r=> r.Name)
*Psuedo-Code*
if((this.Name==next.Name)&&(this.surSame==next.Surname)
toss next data and loop to next
*Psuedo-Code*
.Take(5);
Maybe a bit messy, but you get the idea what I want to achieve. Can I do this in some way or is there any better way to go about it?
You could do this:
var names = db.People
.Where(r => r.Name.Contains(q))
.Select(r => new { Name = r.Name, Surname = r.Surname })
.Distinct()
.Take(5);
But if that won't work because you need the whole People record, you just want the first, I've done something like this with success:
var names = db.People
.Where(r => r.Name.Contains(q))
.GroupBy(r => new { Name = r.Name, Surname = r.Surname })
.Select(g => g.First())
.Take(5);
Distinct utilizing Equals on People class would be the correct way, but here's an alternative that is more "inline":
var names = db.People
.Where(r => r.Name.Contains(q))
.GroupBy(r => new { r.Name, r.Surname })
.Select(g => g.First())
.OrderBy(r => r.Name)
.Take(5);
Use Distinct() and implement the method Equals in People class, or use an auxiliary class to compare them:
public class PeopleComparer : IEqualityComparer<People>
{
public bool Equals(People x, People y)
{
return x.Name == y.Name && x.Surname == y.Surname;
}
public int GetHashCode(People obj)
{
unchecked
{
return (obj.Name.GetHashCode() * 31) + obj.Surname.GetHashCode();
}
}
}

LINQ to SQL GroupBy Max() lambda

Please help me convert this to a lambda expression
SELECT [UserID], MAX(Created) Created
FROM [UserHistory]
WHERE userid IN (14287)
GROUP BY [UserID]
Thanks!
EDIT:
Here's what I have so far.
List<T> list; //list is populated
table.Where(x => list.Select(y => y.ClientId).Contains( x.UserID))
.GroupBy(x => x.UserID)
.Select(x => new { x.UserID, x.Created })
.ToList();
When I add the GroupBy, my Select says there's no definition for x.UserID and x.Created.
Here you go:
var userIDs = new[] {14287, };
var results =
dataContext.UserHistories
.Where(user => userIDs.Contains(user.userID))
.GroupBy(user => user.userID)
.Select(
userGroup =>
new
{
UserID = userGroup.Key,
Created = userGroup.Max(user => user.Created),
});
Regarding your edit, as I said in comment, in .Select(x => new { x.UserID, x.Created }), x is no longer a UserHistory, it is IGrouping<int, UserHistory>(1), which does not have UserID and Created properties, but only Key property, which is an user id and implements IEnumerable to enumerate all items for the given key (user id).
Use #DaveShaw snippet as it is the most correct one and most efficient.
(1) I assume UserID is int.
var query = from history in context.UserHistories
where ( new[] {14287} ).Contains(history.userID)
group history by history.UserID into g
select new
{
UserId = g.Key,
MaxCreated = g.Max( x => x.Created)
};

Categories

Resources