LINQ for 3 tables - new to LINQ - 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}

Related

LINQ perform select and insert in single query

I am working on Entity Framework 6 in C# .NET CORE 2.0 application. I have requirement to get role id from database where roleName = x and add role reference to user as in one: many table
I want to avoid 2 trip to database, I want to do in one go or in single Linq query
UserDataModel userObj = new UserDataModel()
{
Id = fakeUserID,
Name = "k1",
Surname = "z",
Email = "k.z#yahoo.co.uk",
Roles = new List<UserRoleDataModel>
{
new UserRoleDataModel {
UserId = fakeUserID,
RoleId = Context.Roles.Where(roleName => roleName.Name == RoleName).Select(x=>x.Id)
}
}
};
Context.Add<UserModel>(userObj);
Context.SaveChanges();
above code gives me error at RoleId
refer in screen shot;
Error because you are assigning IQueryable to a Property of type Int (I Assumed its Int). You should do this:
RoleId = Context.Roles.Where(roleName => roleName.Name == RoleName && roleName.Id==Id).Select(x=>x.Id).First();

Using AutoMapper for JOIN operation without Foreign key

I have db TABLES:
Company(CompanyId, Name, UserId, CompanyType, Uid)
User(UserId, FirstName, LastName, SocialNumber)
And in application MODEL:
CompanyModel(CompanyId, Name, CompanyType, Uid, UserName)
Since CompanyModel and Company have many same type and name Fields I tend to use AutoMapper to avoid writing explicitly all of those fields.
But here I now need to get UserName.
If UserId had FK I would have been able to do it like this:
var mapper = Mapper.CreateMap<Company,CompanyModel>();
mapper.ForMember(
d => d.UserName,
s => s.MapFrom(a => a.User.FirstName + " " + a.User.LastName));
var q = companyQuery.Project().To<CompanyModel>();
But UserId in Company table don't not have Foreign key relationship constrain with User and I can't create it.
So at the moment I am doing it with join:
var r =
from c in companyQuery
join u in userQuery on c.UserId equals u.UserId
select new CompanyModel
{
CompanyId = c.CompanyId,
Name = c.Name,
CompenyType = c.CompanyType,
Uid = c.Uid,
UserName = u.FirstName + " " + u.LastName
};
I would like to know if there is any way to do this with AutoMapper, and how? so I can skip naming all fields (this is just sample, there are much more fields).
Is anyone familiar with this ?

How to Execute a method inside LINQ to Entities'

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

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

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