How to add condition to LINQ query based on Id - c#

This is my action method which fetches all the users with their Id.
public JsonResult GetUsers()
{
var ret = (from user in db.Users.ToList()
select new
{
UserName = user.UserName,
// i am stuck here, i want to get all those ids whom current logged user is following
Idfollowing = user.FollowTables.Contains()
Idnotfollowing =
});
return Json(ret, JsonRequestBehavior.AllowGet);
}
the structure of FollowTable is like this:
ID UserId FollowId
1 4 11
2 4 12
2 4 13
here, current loggedin user's id is 4 and he is following 11, 12, 13 so i want to return only 11, 12 and 13 to Idfollowing and rest remaining id in the Idnotfollowing. how to get it done.
Well, i think with list or array, i will not get desired result. so, i want to add something here.
Well, with every UserName an id is also passed to view page. So, i have break them into two.Now, how to assign values to these ids.
Comapre User.Id with Current loggedin user's follow table's followId column.If match is found .i.e if id matches or found then assign that user.Id to Idfollowing and null to Idnotfollowing and vice versa in opposite case.
I have to generate follow unfollow button based on these ids returned.
public JsonResult GetUsers()
{
int currentUserId = this.User.Identity.GetUserId<int>();
var ret = (from user in db.Users.ToList()
let Id = user.FollowTables.Where(x => x.UserId == currentUserId).Select(f => f.FollowId).ToList()
let Idnot = (from user2 in db.Users
where !Id.Contains(user2.Id)
select user2.Id).ToList()
select new
{
UserName = user.UserName,
Id = Id,
//Id = user.FollowTables.Where(x => x.UserId == currentUserId)
// .Select(x => x.FollowId).Single(),
Idnot = Idnot,

It looks like you have a standard one-to-many relationship from User to FollowTable. This data model enforces that user.FollowTables only contains followers. You won't be able to fill in Idnotfollowing from the FollowTables property directly.
Something like this may work:
var query = (
from user in db.Users // note: removed ToList() here
// to avoid premature query materialization
where //TODO ADD WHERE CLAUSE HERE ?
let followIds = user.FollowTables.Select(f => f.FollowId)
let notFollowIds = (from user2 in db.Users
where !followIds.Contains(user2.Id)
select user2.Id)
select new
{
UserName = user.UserName,
Idfollowing = followIds.ToArray(),
Idnotfollowing = notFollowIds.ToArray()
})
// TODO add paging? .Skip(offset).Take(pageSize)
.ToList();
Do verify the SQL generated by this query and make sure it performs ok though...
Also, note that I removed the .ToList() from db.Users.ToList() to avoid premature query materialization. It is generally a bad idea anyway to extract all data from a table unconstrained, you will typically want to a

var ret = (from user in db.Users.ToList()
select new
{
UserName = user.UserName,
Idfollowing = user.FollowTables.Select(x=> x.Id)
Idnotfollowing = db.FollowTables.Where(x=> !user.FollowTables.Select(x=> x.Id).Contains(x.Id)).Select(x=> x.Id)
});
it's ugly but will work, there must be another better way to do.

You can simply use a Where method to filter the table and use Select to project FollowiId:-
var ret = (from user in db.Users.ToList()
select new
{
UserName = user.UserName,
Idfollowing = user.FollowTables.Where(x => x.UserId == user.Id)
.Select(x => x.FollowId).ToArray(),
Idnotfollowing = user.FollowTables.Where(x => x.UserId != user.Id)
.Select(x => x.FollowId).ToArray()
});
Assuming, Idfollowing & Idnotfollowing are array if integers (if FollowId is integer) otherwise you can replace it with ToList if its a list instead.

Related

How to return IQueryable LINQ result from two joined tables into a List<string>?

This is an add-on question to one asked here: Entity Framework Core 5.0 How to convert LINQ for many-to-many join to use Intersection table for ASP.NET Membership
How can I return the results of an the following LINQ IQueryable result, which is from two join tables, for the RoleName column to a List<string>?
var queryResult = (this.DbContext.aspnet_UsersInRoles
.Where(x => x.UserId == dpass.UserId)
.Join(
this.DbContext.aspnet_Roles,
ur => ur.RoleId,
r => r.RoleId,
(ur, role) => new
{
ur,
role
}
)
.Select(x => new { x.ur.UserId, x.role.RoleName })
);
UPDATE 1
I need the List in the form of an array of values so that I can use the Contains() method. I need to search for specific RoleNames assigned to a UserId. If I use ToList() on the IQueryable, then the array result is in the form of:
{ RoleName = "admin"}
{ Rolename = "user"}
I am unable to use the .Contains() method because I get the following error:
cannot convert from 'string' to <anonymous type: string RoleName>.
It seems be to expecting a class that the query result can be assigned to. But, one doesn't exist because I am doing this on-the-fly.
UPDATE 2
I need the queryResult in a List that is in the form of:
{ "admin"}
{ "user"}
With this output, I can use the .Contains() method to perform multiple checks. This is used for determining Windows Forms field properties. So, if the UserId belongs to the admin role then the form enables certain check boxes and radio buttons whereas if the UserId belongs to the user role then the form enables different check boxes. This is not an exhaustive list of roles available along with the checks that are performed by the form. But, what is important is that there are multiple checks on the List that need to be performed in separate IF statements.
Currently, I am able to use the queryResult to do the following:
Get a list of the RoleNames
Perform separate LINQ queries on the queryResult by checking for the specific RoleName
Perform a .Count() > 0 check to see if the UserId is in a specific role.
This seems like an ugly hack because I have the intermediate step of creating 1 + N variables to retrieve, by LINQ, and store each RoleName and then check to see if the .Count() is greater than zero. I think that the List method would be cleaner and more efficient. If that is possible.
var varUser = from d in queryResult
where d.RoleName == "user"
select new { d.RoleName };
var varAdmin = from u in queryResult
where u.RoleName == "admin"
select new { u.RoleName };
//... more declarations and LINQs ...
Short answer:
Select only the RoleName, and use SelectMany instead of Select
Better answer
So you have a table of Roles, and a table of Users (I'm simplifying your long identifiers, not part of the problem and way too much typing).
There seems to be a many to many relation between Roles and Users: Every Role is a role for zero or more Users, every User has zero or more Roles.
This many-to-many relation is implemented using a standard junction table: UsersInRoles. This junction table has two foreign keys: one to the User and one to the Roles.
You have a UserId, and it seems that you want all names of all Roles of the user that has this Id.
How about this:
int userId = ...
// Get the names of all Roles of the User with this Id
var namesOfRolesOfThisUser = dbContext.UsersInRoles
// only the user with this Id:
.Where(userInRole => userInRole.UserId == userId)
// get the names of all Roles for this userInRole
.SelectMany(userInRole => dbContext.Roles.Where(role => role.RoleId == userInRole.RoleId)
.Select(role => role.RoleName));
In words: from the table of UsersInRoles, keep only those UsersInRoles that have a value for property UserId that equals userId.
From every one of the remaining UsersInRoles, select all Roles that have a RoleId that equeals the UserInRole.RoleId. From these Roles take the RoleName.
I use SelectMany to make sure that I get one sequence of strings, instead of a sequence of sequences of strings.
If you suspect double RoleNames, consider to append Distinct() at the end.
But I want to Join!
Some people really like to do the joins themselves.
int userId = ...
var namesOfRolesOfThisUser = dbContext.UsersInRoles
.Where(userInRole => userInRole.UserId == userId)
.Join(dbContext.Roles,
userInRole => userInRole.RoleId, // from every UserInRole take the foreign key
role => role.RoleId, // from every Role take the primary key
// when they match, take only the name of the Role
(userInRole, role) => role.RoleName);
Try to use GroupBy(). Be careful, this method is not supported by direct IQueryable to SQL conversion. If you will try to call GroupBy() before .ToList(), it will throw an error.
In your example you could this: select a list in memory and then work with it:
var queryResult = (this.DbContext.aspnet_UsersInRoles
.Where(x => x.UserId == dpass.UserId)
.Join(this.DbContext.aspnet_Roles,
ur => ur.RoleId,
r => r.RoleId,
(ur, role) => new { ur, role }
)
.Select(x => new { x.ur.UserId, x.role.RoleName })
.ToList() // MATERIALIZE FIRST
.GroupBy(x => x.UserId) //ADD THIS
);
queryResult.Contains(roleName=> roleName == "ROLE_TO_SEARCH")
var userId = queryResult.Key;

C# Collection Filtering, where do I begin

I have a collection lets call it 'Users', aka a set of USER
A USER has a set of CONTACTs.
I simply want to get all CONTACTs for a given user in common with another user.
I'm learning about Lambda/Linq expressions but not sure if thats what I should be doing to solve this sort of collection filtering problem.
So really I'm just looking for a intersection of 2 sets.
You can't simply do "==" on two collections. However, if the contacts have some kind of id and a user can't have the same contact twice, this is pretty easy to do:
User user = ...
var contactIds = user.Contacts.Select(c => c.Id).ToArray();
var usersWithMatchingContacts = this.unitOfWork.UserRepository.Get()
// basically, we're checking that the number of matching contacts is the same as the
// total number of contacts for the user, which means that the user has the same
// set of contacts. If you just want to require some overlap, change
// the "== u.Contacts.Count" to "> 0"
.Where(u => u.Contacts.Count(c => contactIds.Contains(c.Id)) == u.Contacts.Count);
//input data
var firstId = 12;
var secondId = 23;
var firstUser = users.First(user => user.Id == firstId);
var secondUser = users.First(user => user.Id == secondId);
var commonContacts = firstUser.Contacts
.Where(contact =>
secondUser.Contacts.Contains(contact))
Use LINQs Intersect method
var commonContacts = firstUser.Contacts.Intersect(secondUser.Contacts)

`from..where` or `FirstOrDefault` in LINQ

Traditionally, when I've tried to get data for a user from a database, and I've used the following method (to some degree):
DbUsers curUser = context.DbUsers.FirstOrDefault(x => x.u_LoginName == id);
string name = curUser.u_Name;
string email = curUser.u_Email;
You can see that all I want to do is get the Name and Email, but it seems to me that this LINQ query is getting everything stored in the database of that user, bringing it back, then allowing me to get what I want.
I have been doing some research and have found the following alternative:
var current = from s in context.DbUsers
where s.u_LoginName == id
select new {
name = s.u_Name,
email = s.u_Email
};
foreach (var user in current)
{
//Stuff Here
}
Which would be better, if any at all? Is there a lighter method to use when I only want to retrieve a few results / data?
If you want to get only two fields, then you should project your entity before query gets executed (and in this case query gets executed when you call FirstOrDefault). Use Select operator for projection to anonymous object with required fields:
var user = context.DbUsers
.Where(u => u.u_LoginName == id)
.Select(u => new { u.u_Name, u.u_Email })
.FirstOrDefault(); // query is executed here
string name = user.u_Name; // user is anonymous object
string email = user.u_Email;
That will generate SQL like:
SELECT TOP 1 u_Name, u_Email FROM DbUsers
WHERE u_LoginName = #id
In second case you are doing projection before query gets executed (i.e. enumeration started). That's why only required fields are loaded. But query will be slightly different (without TOP 1). Actually if you will convert second approach to lambda syntax, it will be almost same:
var query = context.DbUsers
.Where(u => u.u_LoginName == id)
.Select(u => new { u.u_Name, u.u_Email });
// query is defined but not executed yet
foreach (var user in query) // executed now
{
//Stuff Here
}
And just to show complete picture, without projection you get all fields of first found user:
DbUsers user = context.DbUsers
.Where(u => u.u_LoginName == id)
.FirstOrDefault(); // query is executed here
string name = user.u_Name; // user is DbUsers entity with all fields mapped
string email = user.u_Email;
In that case user entity is not projected before query is executed and you'll get all fields of user loaded from database and mapped to user entity:
SELECT TOP 1 u_LoginName, u_Name, u_Email /* etc */ FROM DbUsers
WHERE u_LoginName = #id
The second is better. You only get the needed data from database so the network traffic is lighter.
You can have the same result with extension methods:
var user = context.DbUsers
.Where(x => x.u_LoginName == id)
.Select(x => new {...})
.FirstOrDefault();
If you need not whole entity, but some values from it, then use new {name = s.u_Name, email = s.u_Email}. Because, this object is much "lighter" for cunstruction.
When you get entity with FirstOrDefault, it' saved in DBContext, but you don't do anything with it.
So, i advice you to get only data you need.

LINQ request many-to-many

I have the following tables
Users
- ID
- FirstName
- LastName
MultiplyItems
- ItemID
- Title
UserMultiplyItems
- UserID
- ItemID
I have a variable
List<int> delegateList = {1, 3, 5};
where 1, 3, 5 are ItemID
I want to select all users, where at least one ItemID linked selectable user.
I try the following:
var result = from i in _dbContext.Users
where
((delegateList == null) || i.MultiplyItems.Any(p=> delegateList.Any(a => a == p.ItemID)))
select new UserModel()
{
....
};
but it does not work. error:
Cannot compare elements of type 'System.Collections.Generic.List`1'.
Only primitive types, enumeration types and entity types are
supported.
How to do it correctly?
Thanks
I would write this one:
var filteredUsers = delegateList == null
? _dbContext.Users
: _dbContext.Users.Where(user => user.MultiplyItems
.Any(item => delegateList.Contains(item.Id)));
var result = filteredUsers.Select(user => new UserModel
{
//the UserModel initialization
});
You should not check the following line inside the query:
delegateList == null
It is translated to SQL, and SQL has no idea about what is List and how to compare it with null.
var result = from i in _dbContext.Users
from mi in i.multiplyItems
select new yourClass();
if(delegateList!=null)
{
result = result.Where(delegateList.contains(mi.ItemID));
}
result.ToList();
I dont have visual studio open to test, but it should be a lot like that.
I'm not exactly sure if this is what you wanted, but i thought its better to try and help.
This will output all users from the Users Table where the ItemID is contained in the delegateList. The magic lays in the Contains operator you can get elements from list a contained in list b
var selection = from a in db.UserMultiplyItems
from b in db.Users
where delegateList.Contains(a.ItemID) && a.UserID == b.ID
select b;
Try changing it to:
var result = from u in _dbContext.Users
where
((delegateList == null) || u.MultiplyItems.Any( mi => delegateList.Contains(mi.ItemID)))
select new UserModel()
{
....
};
Note I've also renamed your "i" and "p" things to "u" and "mi" to make it easier to read.
Alternatively you could not use LINQ at all and just stick with lambda expressions:
List<UserModel> usersWithItems =
context
.Users
.Where(u => u.MultiplyItems.Any(mi => (delegateList == null) || (delegateList.Contains(mi.ItemID))))
.Select(um => (new UserModel() { ... } ) )
.ToList();
Which personally I prefer, it means you don't need to know linq at all.

How can i do it using linq2sql...?

I have a table Users. Users has a column rating. How i can get information about user place using linq2sql? I want method like:
var userPlace =
GetUserPlaceById(userId);
Table Users may contains a few thousands users.
Sorry guys. Users DOESNT contain place column. Real example: Rating is chess elo rating. if you have high rating then you on 1st place. If you have lower rating then you on the last place.
Did you mean something like this?
int userRating = users.Single(user => user.Id = userId).Rating;
int userPlace = users.Where(user => user.Rating < userRating).Count() + 1;
I have a table Users. Users has a column rating. How i can get information about user place using linq2sql?
I'm not sure what "userPlace" is, but assuming it is a column in that table...
var userPlace = (from user in db.Users
where user.Id == userId
select user)
.First()
.UserPlace;
Be aware that calling .First() will throw an exception if no match is found, so if you expect that sometimes this user will not exist use FirstOrDefault, check for null, and then grab the UserPlace property.
You would use something like:
string GetUserPlaceById(int userId)
{
IQueryable<User> users = GetUsers(); // Get users queryable reference
return users.Single(user => user.Id == userId).Place;
}
You could do something like this:
var userPlace = _db.Users.Where(x => x.UserId == userId).Select(x => x.Place).SingleOrDefault();

Categories

Resources