How to Execute a method inside LINQ to Entities' - linq

var users = from u in db.UserProfiles select new UsersViewModel{
UserName = u.UserName,
UserId = u.UserId,
IsDisabled = u.IsDisabled,
Role = Roles.GetRolesForUser(u.UserName).FirstOrDefault()
};
I would like to select the roles from the database and create a list of UsersViewModel. However Entity Framework is trying to execute the projection on the SQL side, where there is no equivalent to Roles.GetRolesForUser.
What would be an alternative to this or how am I suppose to execute any method inside the query?

The easiest is to get the data you want from SQL, then after the query executes, iterate through the results and populate the additional details from the function in your code.
Example:
var users = (from u in db.UserProfiles select new UsersViewModel{
UserName = u.UserName,
UserId = u.UserId,
IsDisabled = u.IsDisabled
}).ToList();
foreach(var user in users){
user.Role = Roles.GetRolesForUser(u.UserName).FirstOrDefault();
}
The key to remember here is to separate out what you're doing (understanding the separation of concerns in your architecture). Take care of the SQL first, then augment the data from other sources, in your case the Role Provider.

You can force the query to execute before creating the ViewModels by adding ToList():
var users = from u in db.UserProfiles.ToList()
select new UsersViewModel{
UserName = u.UserName,
UserId = u.UserId,
IsDisabled = u.IsDisabled,
Role = Roles.GetRolesForUser(u.UserName).FirstOrDefault()
};
As CodeMonkey1313 noted, I strongly suggest you apply some sort of filter in your query:
var users = from u in db.UserProfiles
.Where( x => /* apply filter here */ )
.ToList() //Force query to execute
select new UsersViewModel {
UserName = u.UserName,
UserId = u.UserId,
IsDisabled = u.IsDisabled,
Role = Roles.GetRolesForUser(u.UserName).FirstOrDefault()
};

You can convert your Queryable to an Enumerable that executes locally:
var users = (from u in db.UserProfiles select new UsersViewModel{
UserName = u.UserName,
UserId = u.UserId,
IsDisabled = u.IsDisabled)}
).AsEnumerable()
.Select(u => new {
UserName = u.UserName,
UserId = u.UserId,
IsDisabled = u.IsDisabled,
Role = Roles.GetRolesForUser(u.UserName) })
.FirstOrDefault()

Related

LINQ for 3 tables - new to LINQ

I have 3 tables
Users - UserID, Firstname, LastName, etc
Roles - RoleId, RoleName
Features - UserId, RoleId
I want to display Users with common features using LINQ
Please help
you can try a lookup:
//Associate the role names with the features
var featureRoles = dbContext.Roles.Join(dbContext.Features,
role => role.RoleId,
feature => feature.RoleId,
(role, feature) => new {
RoleId = feature.RoleId,
UserId = feature.UserId,
RoleName = role.RoleName
});
//Create ILookup
var lookup = dbContext.Users.Join(featureRoles,
user => user.UserID,
fr => fr.UserId,
(user, fr) => new {
User = user,
RoleName = fr.RoleName,
RoleId = fr.RoleId
})
.ToLookup(r => r.RoleName, s => s.User);
This will sort your users by their role name for example:
var users = lookup["role 1"];
foreach(var user in users)
{
Console.WriteLine("Users in Role 1:");
Console.WriteLine("\t{0} {1}", user.Firstname, user.Lastname);
}
Output:
"Users in Role 1:"
"John Smith"
"Foo Bar"
...
If you want to search by the role id in stead of the name then you will need to alter the lookup definition to .ToLookup(r=> r.RoleId, s=> s.User)
from u in Users
join f in Features on u.UserId equals f.UserId
join r in Roles on r.RoleId equals f.RoleId
where f.RoleId == 1
select new { u.FirstName,u.LastName,r.RoleName}

Using LINQ to pull MembershipUser.Username into a new object

I have the following code and I need to pull out each user's username from the membership tables:
var results = (from u in db.U_USER
where u.Forename.ToLower().Contains(search)
|| u.Surname.ToLower().Contains(search)
select new ExUser
{
MembershipId = u.MembershipId,
Surname = u.Surname,
Forename = u.Forename,
Username = Membership.GetUser(u.MembershipId).UserName
});
Now I know why im getting the following, but can anybody recommend a solution?
LINQ to Entities does not recognize the method 'System.Web.Security.MembershipUser GetUser
LINQ to Entities is trying to translate your select expression into T-SQL, which it can't because there's no ExUser etc. in T-SQL.
Finhish off the expression with a call to AsEnumerable or similar, and then perform the projection:
var results = (from u in db.U_USER
where u.Forename.ToLower().Contains(search)
|| u.Surname.ToLower().Contains(search)
select u)
.AsEnumerable()
.Select(u => new ExUser
{
MembershipId = u.MembershipId,
Surname = u.Surname,
Forename = u.Forename,
Username = Membership.GetUser(u.MembershipId).UserName
});
You can do it by forcing the results into memory by using AsEnumerable. When the results are in memory you can call Membership.GetUser without linq attempting to translate it to sql:
var results = (from u in db.U_USER
where u.Forename.ToLower().Contains(search) || u.Surname.ToLower().Contains(search)
).AsEnumerable().Select(u => new ExUser {
MembershipId = u.MembershipId,
Surname = u.Surname,
Forename = u.Forename,
Username = Membership.GetUser(u.MembershipId).UserName
});

Linq To Entities - how to filter on child entities

I have entities Group and User.
the Group entity has Users property which is a list of Users.
User has a property named IsEnabled.
I want to write a linq query that returns a list of Groups, which only consists of Users whose IsEnabled is true.
so for example, for data like below
AllGroups
Group A
User 1 (IsEnabled = true)
User 2 (IsEnabled = true)
User 3 (IsEnabled = false)
Group B
User 4 (IsEnabled = true)
User 5 (IsEnabled = false)
User 6 (IsEnabled = false)
I want to get
FilteredGroups
Group A
User 1 (IsEnabled = true)
User 2 (IsEnabled = true)
Group B
User 4 (IsEnabled = true)
I tried the following query, but Visual Studio tells me that
[Property or indexer 'Users' cannot be assigned to -- it is read only]
FilteredGroups = AllGroups.Select(g => new Group()
{
ID = g.ID,
Name = g.Name,
...
Users = g.Users.Where(u => u.IsInactive == false)
});
thank you for your help!
There is no "nice" way of doing this, but you could try this - project both, Group and filtered Users onto an anonymous object, and then Select just the Groups:
var resultObjectList = AllGroups.
Select(g => new
{
GroupItem = g,
UserItems = g.Users.Where(u => !u.IsInactive)
}).ToList();
FilteredGroups = resultObjectList.Select(i => i.GroupItem).ToList();
This isn't a documented feature and has to do with the way EF constructs SQL queries - in this case it should filter out the child collection, so your FilteredGroups list will only contain active users.
If this works, you can try merging the code:
FilteredGroups = AllGroups.
Select(g => new
{
GroupItem = g,
UserItems = g.Users.Where(u => !u.IsInactive)
}).
Select(r => r.GroupItem).
ToList();
(This is untested and the outcome depends on how EF will process the second Select, so it would be nice if you let us know which method works after you've tried it).
I managed to do this by turning the query upside down:
var users = (from user in Users.Include("Group")
where user.IsEnabled
select user).ToList().AsQueryable()
from (user in users
select user.Group).Distinct()
By using the ToList() you force a roundtrip to the database which is required because otherwise the deferred execution comes in the way. The second query only re-orders the retrieved data.
Note: You might not be able to udpate your entities afterwards!
try something like this and you'll still have your entities:
FilteredGroups = AllGroups.Select(g => new
{
Group = g,
Users = g.Users.Where(u => u.IsInactive == false)
}).AsEnumerable().Select(i => i.Group);
That way you should still be able to use Group.Users
If you want to retain your entity structure, try this:
var userGroups = context.Users.Where(u => !u.IsInactive).GroupBy(u => u.Group);
foreach (var userGroup in userGroups)
{
// Do group stuff, e.g.:
foreach (var user in userGroup)
{
}
}
And you certainly can modify your entities!
Use inner linq query
var FilteredGroups = (from g in AllGroups
select new Group()
{
ID = g.ID,
Name = g.Name,
...
Users = (from user in g.Users
where user.IsInactive == false
select user).ToList()
});

How to use join to make this queries cleaner?

I have the feeling that using joins could make this cleaner
public override string[] GetRolesForUser(string username)
{
using (TemplateEntities ctx = new TemplateEntities())
{
using (TransactionScope tran = new TransactionScope())
{
int userId = (from u in ctx.Users
where u.UserName == username
select u.UserId).Single();
int[] roleIds = (from ur in ctx.UserInRoles
where ur.UserId == userId
select ur.RoleId).ToArray();
string[] roleNames = (from r in ctx.Roles
where roleIds.Contains(r.RoleId)
select r.RoleName).ToArray();
tran.Complete();
return roleNames;
}
}
}
You should be able to use the navigation properties to follow the relations instead of using the primary keys (Entity Framework will join behind the scenes for you)
If you have (and need) UserInRoles because there are other properties defined on the junction table, you can use:
return (from u in cts.Users
from ur in u.UserInRoles
from r in ur.Roles
select r.roleName).ToArray();
Otherwise make sure the N-M relation is mapped as such, and don't map the junction table. Then you can just use:
return (from u in cts.Users
from r in u.Roles
select r.roleName).ToArray();
I'm not a c# guy, but essentially you would want to do
select u.userId, ur.roleId, r.roleName
from Users u, UserInRoles ur, Roles r
where u.userId = ? and ur.userId = u.userId and r.roleId = ur.roleId;
You can also use the in syntax if you opt for nested queries.
ie: where user_id in (select userId from UserInRoles)

LINQ grouping/subquery to fill a hierarchy data strcuture

I have a DataTable that queries out something like below
usergroupid...userid......username
1.............1...........John
1.............2...........Lisa
2.............3...........Nathan
3.............4...........Tim
What I'm trying to do is write a LINQ statement that will return an array of UserGroup instances. The UserGroup class has properties of UserGroupId and Users. Users is an array of User instances. The User class then has properties of UserId and UserName.
Can filling such a hierarchy be done with a single LINQ statement and what would it look like?
Thanks a million
Check this out, hope this helps
var users = new[]
{
new {UserGroupId = 1, UserId = 1, UserName = "John"},
new {UserGroupId = 1, UserId = 2, UserName = "Lisa"},
new {UserGroupId = 2, UserId = 3, UserName = "Nathan"},
new {UserGroupId = 3, UserId = 4, UserName = "Tim"}
};
var userGroups = from user in users
group user by user.UserGroupId into userGroup
select new {
UserGroupId = userGroup.Key,
Users = userGroup.ToList()
};
foreach (var group in userGroups)
{
Console.WriteLine("{0} - {1}",group.UserGroupId, group.Users.Count);
}
There is - look at GroupBy and Select methods.

Resources