I am trying to populate my WebGrid from LINQ to SQL.
It has the following columns:
Username
Email
First Name
Last Name
IsApproved
IsLockedOut
IsOnline
LastActivityDate
LastLoginDate
CreationDate
All of this comes from the User object:
dbc.Users
However everything except First Name and Last Name is an association from aspnet_Membership and aspnet_User which is defined in my LINQ to SQL. How do I set these as the proper column names?
I tried:
var accounts = dbc.Users.Select(User => new User
{
Username => aspnet_Membership.username
}).ToList();
For username but it is not working.
How would I do this?
It would be:
var accounts = dbc.Users.Select(User => new User
{
Username = aspnet_Membership.username
}).ToList();
No => for property assignment, but assuming that's not the case and what you need is to "flatten" the resultset between multiple objects, one way to work around having one entity with multiple tables is to manage this by using an anonymous class:
var accounts = from u dbc.Users
let m = dbc.aspnet_Memberships.First(i => i.ProviderUserKey == u.UserKey)
select new
{
Username = m.username,
FirstName = u.FirstName
}).ToList();
Or, define a view, and add this to your model. This is another way to have a common entity.
Related
I have the following table relations
User
-List<Role> on User.RoleId = Id
-RoleDetail on Role.RoleDetailId = Id
-Practice on Role.PracticeId = Id
While I do have all these entities linked up in EF, I'm only able to access Roles when I query User with dbConext Include, RoleDetail & Practice are not accessible, which is understandable. Is there any way I can change how I query User to include those information?
If not, how should I join these tables efficiently? I'm not looking to foreach loop on every single user's multiple roles but a single query request. Any help/hint is appreciated.
Querying User with Role
users = _dbcontext.Users.Include(u => u.Roles)
Joining
var userQuery = _userRepository.GetUser(searchTerm);
var roleQuery = _roleRepository.GetRole();
var test = from user in userQuery join role in roleQuery on user.Roles....
You need to use two Include on roles for getting all data like this:
users = _dbcontext.Users.Include(u => u.Roles).ThenInclude(x=>x.RoleDetail)
.Include(u => u.Roles).ThenInclude(x=>x.Practice)
.AsSplitQuery(); //highly recommend if you are using .Net >=5
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;
I am trying to do something simple, but I can't figure it out. Using EF6, I have 2 tables which are associated by an intermediate associative table, giving a many-to-many relationship:
I want to query all users who belong to a list of families. So I get an array of families:
var db = new MyProjectEntities();
User user = GetUserById((int)HttpContext.Current.Session["CurrentUserId"]);
var families = db.Users.Where(u => u.UserId == user.UserId).First().Families.ToArray();
Then I want to query all users belonging to these families:
var users = db.Users.Where(u => families.Contains(u.Families));
But I get this error:
Instance argument: cannot convert from 'Database.Family[]' to 'System.Linq.IQueryable>'
Thanks in advance.
You can use Any method:
var users = db.Users.Where(u => u.Families.Any(fam => families.Contains(fam))).AsEnumerable();
I think this is what you're searching for.
Say I have a set of Sites that have a collection of Users. I find myself violating the DRY principal whenever I have to redefine a query to get, say, the last visited User for a given site.
For example, my query may look like this:
from site in Context.Sites
where site.ID == 99
select new {
ID = site.ID,
Name = site.Name,
URL = site.URL,
LastVisitedUser = site.Users.OrderByDescending(u => u.LastVisited).Select(u => new {
ID = u.ID,
Username = u.Username,
Email = u.EmailAddress
})
.FirstOrDefault()
}
That query returns what I want, but I find myself repeating this same select for the LastVisitedUser in multiple places as I'm selecting the site in different ways to populate my various ViewModels.
So, I thought that I would simply extend my Site Entitiy class with a property like so:
public partial class Site {
public LastVisitedUser {
get {
var query = from user in Users
where user.SiteID == this.ID
orderby user.LastVisited descending
select user;
return query.FirstOrDefault()
}
}
}
In this manner, whenever I am selecting a site it would be fairly trivial to grab this property. This almost works, however I am stuck trying to assign an Entity user into my UserViewModel property into the LastVisited property of my return, without an obvious way on how to project the User into my ViewModel version.
Perhaps an example would help explain. What I'm trying to accomplish would be something like this:
from site in Context.Sites
where site.ID == 99
select new SiteViewModel {
ID = site.ID,
Name = site.Name,
URL = site.URL,
LastVisitedUser = site.LastVisitedUser <--- NOTE
}
NOTE = This is my point of failure. LastVisitedUser is a ViewModel with a subset of User data.
Am I going about this in the correct manner? Can I achieve what I'm trying to do, or am I horribly misguided? Am I about to sove this issue and run into others?
Thanks!
Edit: The former answer was not correct. You cannot use extension method on the navigation property but you can still create extension method for the whole Site projection.
Just create simple reusable extension method:
public static IQueryalbe<SiteModel> GetSiteModels(this IQueryable<Site> query)
{
return query.Select(site => new SiteModel {
ID = site.ID,
Name = site.Name,
URL = site.URL,
LastVisitedUser = site.Users
.OrderByDescending(u => u.LastVisited)
.Select(u => new LastVisitedUser {
ID = u.ID,
Username = u.Username,
Email = u.EmailAddress
}});
}
Now you should be able to use that extension in your former query:
Context.Sites.Where(site => site.ID == 99).GetSiteModels();
Your example will not work because your property is not visible for Linq-to-entities.
If you mean that you what to reuse common queries with different extensions, you just nead to write some base query and get different results and some Where lambda expressions. If you profile it, you will see that you have just one query to DB just return IQuerable in a base query
I would like to get the list of users from the database, but I want only 5 columns instead of all (it has about 35 columns). When I wrote like the following, it shows me no error at the compile time but the error at the runtime.
bksb_Users is the table name in my database as well as object name in the Entity Model.
public List<bksb_Users> SearchStudents(string reference, string firstname, string lastname)
{
return (from u in context.bksb_Users
where u.userName.Contains(reference)
&& u.FirstName.Contains(firstname)
&& u.LastName.Contains(lastname)
orderby u.FirstName, u.LastName
select new bksb_Users
{
user_id = u.user_id,
userName = u.userName,
FirstName = u.FirstName,
LastName = u.LastName,
DOB = u.DOB
}).Take(100).ToList<bksb_Users>();
}
The error is...
The entity or complex type 'bksbModel.bksb_Users' cannot be constructed in a LINQ to Entities query.
Does below work?
public List<bksb_Users> SearchStudents(string reference, string firstname, string lastname)
{
var anon = (from u in context.bksb_Users
where u.userName.Contains(reference)
&& u.FirstName.Contains(firstname)
&& u.LastName.Contains(lastname)
orderby u.FirstName, u.LastName
select new
{
user_id = u.user_id,
userName = u.userName,
FirstName = u.FirstName,
LastName = u.LastName,
DOB = u.DOB
}).Take(100).ToList();
return anon.Select(z => new bksb_Users()
{
user_id = z.user_id, userName = z.userName, FirstName = z.FirstName, DOB = z.DOB
}).ToList();
}
All I have done is split the task into two steps:
Get the data out (into an anonymous type) using LINQ to Entities.
Convert the anonymous type into the desired type using LINQ to
Objects.
Note a better option would be to create a new type (class) that contains just the fields/properties you need - that would remove the need for step 2, and will make it clear to the callers of your function which columns are 'populated' and which aren't. It also means you are less likely to 'accidentally' try and persist these half populated entities back to the database.
for some reason i quess that field DOB looks something like this
public object DOB { get { return fieldX + fieldY } }
Entity framework does not understand that. All fields in query must be mapped with certain columns in DB