Joining LINQ results with IEnumerable - c#

I have an API written in .NET 4.5 using Entity Framework. Here is what one of the methods looks like:
private myEntities db = new myEntities();
// GET api/User
public IEnumerable<User> GetUsers()
{
return from u in db.Users
select u;
}
Pretty simple. This table can join with a Roles table, so a User can have many Roles. The foreign key UserId is in the Roles table. Here's what I'm trying to do:
private myEntities db = new myEntities();
// GET api/User
public IEnumerable<User> GetUsers()
{
return from u in db.Users
join r in db.Roles on u.UserId equals r.UserId
select new User
{
UserId = u.UserId,
Roles = u.Roles
};
}
This throws an error: The entity or complex type 'ppadModel.User' cannot be constructed in a LINQ to Entities query
How do I write a query that returns the User and all the Roles associated with that user?

You use the .Include extension method.
Like this:
return from u in db.Users.Include("Roles")
select u;
You can also use lamba expression to avoid strings:
return from u in db.Users.Include(x => x.Roles)
select u;
This tells the Entity Framework to load all users and additional all associated roles for the users.

Related

Dynamics 365 OrganizationServiceContext Linq Query with multiple Joins

I am running into an issue running most of my queries. I can generate a link query with a join to only one related entity at a time. But when I run a LINQ query with multiple joins as in the example below, I get the "Sequence contains no elements" error.
var query =
(
from permission in context.CreateQuery<ServiceModel.Types.idoe_permission>()
join contact in context.CreateQuery<ServiceModel.Types.Contact>()
on permission.idoe_contact_idoe_permission.Id equals contact.Id
join corporation in context.CreateQuery<ServiceModel.Types.idoe_corporation>()
on permission.idoe_idoe_corporation_idoe_permission.Id equals corporation.Id
join role in context.CreateQuery<ServiceModel.Types.idoe_role>()
on permission.idoe_idoe_role_idoe_permission.Id equals role.Id
where contact.idoe_ADB2CID == request.UserId
select new { Corporation = corporation, Role = role }
).ToList();
I am only able to "join" one entity at a time. Other examples I have seen allow multiple joins, but I have not been able to get this to work.
Any suggestions?
It looks like you're using the relationship names rather than the lookup field names in the joins. Using the lookup field names instead might look something like this:
var query = (from permission in context.CreateQuery<ServiceModel.Types.idoe_permission>()
join contact in context.CreateQuery<ServiceModel.Types.Contact>() on permission.idoe_contactid.Id equals contact.Id
join corporation in context.CreateQuery<ServiceModel.Types.idoe_corporation>() on permission.idoe_corporationid.Id equals corporation.Id
join role in context.CreateQuery<ServiceModel.Types.idoe_role>() on permission.idoe_roleid.Id equals role.Id
where contact.idoe_ADB2CID == request.UserId
select new { Corporation = corporation, Role = role }).ToList() ;

EntityFramework (.NET Core) left outer join by multiple conditions

I need to match User and UserAction by several criteria: UserId, ActionType, Approved, and still want to keep the query as left outer join, because the action could be missing. In regular .net entity framework I would do the following:
var q = from u in db.User
join ua in db.UserActions on { u.Id, "Approved" } equals { ua.UserId, ua.ActionType } into actions
from ua in action.DefaultIfEmpty()
where u.Active
select new { User = u, Actions = ua}
For the Core version of Entity Framework, unfortunately, it doesn't work. How can I achieve the similar goal using EF for .NET Core?
Try this:
var q = from u in db.User
join ua in db.UserActions on new { UserId = u.Id, ActionType = "Approved" } equals new { ua.UserId, ua.ActionType } into actions
from ua in actions.DefaultIfEmpty()
where u.Active
select new { User = u, Actions = ua}
Property names of the anonymous types on either side of the join syntax need to match.

Acessing UserRoles table in aspnet identity via linq

So i can access the User table, but i need to do a further query within my linq statement to check to see if they have been added into a particular user role
var managers = (from a in db.Users
join b in db.**UserRoles** on a.Id equals b.Id
where a.IsManager == true select a).ToList();
I know you can access it via the html using User.IsInRole, but i really need to access it via my DB call
any help would be great
Given that you are using asp.net identity with code-first approach? you can access the users or Roles tables individually (db.Users/db.Roles). If you want to access roles for a user you could do db.Users.Roles. However, this would only return id's for your roles (there are no navigationproperty from here..)
To check for users in role by role-name, you could join user.Roles with db.Roles, and check the role name from db.Roles. Something like this:
using (var db = new ApplicationDbContext())
{
var user1 = new ApplicationUser { Email = "user1#test.com", UserName = "User #1"};
var user2 = new ApplicationUser { Email = "user2#test.com", UserName = "User #2 - No Roles" };
var role1 = new IdentityRole("SomeRole");
db.Users.Add(user1);
db.Users.Add(user2);
db.Roles.Add(role1);
db.SaveChanges();
user1.Roles.Add(new IdentityUserRole { RoleId = role1.Id });
db.SaveChanges();
var usersInRole = db.Users.Where(u =>
u.Roles.Join(db.Roles, usrRole => usrRole.RoleId,
role => role.Id, (usrRole, role) => role).Any(r => r.Name.Equals("SomeRole"))).ToList();}
Alternatively you could first fetch the Role and then check
var usersinRole = db.Users.Where(u => u.Roles.Any(r => r.RoleId.Equals(someRoleId)));
Depending on which version of you are using you should have some tables like this:
You will need to join to the users in roles table and the roles table.

How to write a LINQ query to select from a collection with given set of matching IDs

I'm using a self tracking entity model. ProductInstallation is a DTO which contains all the details about the product installation for a company.
The UserRoles entity holds the relationship in-between the Product-System Role-UserID.
As an example:
Product: Inventory
System Role : PurchasingUser
User ID : hasithaH <- (Suppose me)
using the below LINQ query, I can get the distinct UserIDs.
string[] userIDs = productInstallation.UserRoles
.Select(u=>u.UserID).Distinct().ToArray();
now I need to get all the User Profiles for the UserIDs I queried in above steps.
productInstallation.SystemUsers = context.SystemUsers.Select(u=> u.UserID ..???
In SQL point of view, this is the query I want:
Select * from SystemUsers where UserID in ('UserA','UserB','UserC')
How should I write a LINQ query to get this done?
You write it as follows:
var result = context.SystemUsers.Where(su =>
productInstallation.UserRoles.Any(ur => su.UserID == ur.UserId));
Or if both sources are not IQuerable from the same db:
string[] userIDs = productInstallation.UserRoles
.Select(u=>u.UserID).Distinct().ToArray();
var result = context.SystemUsers.Where(su =>
userIDs.Contains(su.UserID));
What you really want to do here is join the two tables. Using a Join you can do this in one query rather than executing two separate queries:
var systemUsers = from userRole in UserRoles
join systemUser in SystemUsers
on userRole.UserID equals systemUser.UserID
select systemUser;
You can try this:
productInstallation.SystemUsers =
context.SystemUsers.FindAll(u=> userIDs.Contains(u.UserID))

display it into the "Table1" table

Here are the methods mentioned above:
public IList<tst> testUsers()
{
IList<tst> testUsers = _test.GetAll().ToList();
return test(test);
}
To display Users with Location I think you need one class called AdsnapshotUsers
public class AdsnapshotUsers
{
// three fields UserId, UserLogonName, Location
}
Now create one method which return IList<AdsnapshotUsers>
public IList<AdsnapshotUsers> GetAdsnapshotUsers()
{
List<User> Users = GetAcitveUsers().ToList();
List<ADSnapshot> adSnapshotUsers = _adSnapshotRepository.GetAll().ToList();
return (from u in Users
join ad in adSnapshotUsers on u.UserLogonName equals ad.UserLogonName
select new AdsnapshotUsers {
UserId= u.UserId, UserLogonName = u.UserLogonName, Location = ad.Location
}
).ToList<AdsnapshotUsers>();
}
Left Outer Join to display all the values from the user table eventhough if a userlogonname is not present in the adsnapshot table (location value blank)
(from u in Users
join ad in adSnapshotUsers on u.UserLogonName equals ad.UserLogonName into aduserselect
from ad1 in aduserselect.DefaultIfEmpty()
select new AdsnapshotUsers {
UserId= u.UserId, UserLogonName = u.UserLogonName, Location = ad1.Location
}
).ToList<AdsnapshotUsers>();
Here all records from user table will get selected and for location if userlogonname exist then location name value set with ADSnapshot table value otherwise not exist then default empty value set.
The simplest way to join two IEnumerables in LINQ is with Join():
var joined = from u in GetActiveUsers()
join ad in _adSnapshotRepository.GetAll()
on u.UserLogonName equals ad.UserLogonName
select new { User = u, Location = ad.Location }
Of course, joined is now an IEnumerable of an anonymous type. If you are displaying this information in a grid view or something that requires a flat object, you may want to create your own class with the properties you want from each table and select that instead.
Two other things to note:
(1) you always seem to call ToList() early to convert from IQueryable to IEnumerable. By putting off ToList() until you've done all your joining and filtering, you can get most of your query to happen in SQL (rather than in memory).
(2) rather than joining, it is often preferable to set up associtation properties on your entities and use those:
IQueryable<User> users = ...
var combinedInfo = users.Select(u => new { User = u, Location = u.ADSnapshot.Location })
.ToList();
If you have relationship (1:1) between these two tables then you can get records very easily using just one query. and evenif records are not present in second table.
I am using above defined class definition to store data.
_userRepository.GetAll().Select(u=>new AdsnapshotUsers {
UserId= u.UserId,
UserLogonName = u.UserLogonName,
Location = u.ADSnapshot.Location
}).ToList();

Categories