C# Mapping Many To Many - c#

I'm working on a basic application in C# Web.
I would like to have 2 objects:
- User
- Group
Here's the class:
public class User {
public int Id; { get; set; }
public String Username { get; set; }
public String Password { get; set; }
public virtual List<Group> Groups { get; set; }
}
public class Group{
public int Id { get; set; }
public String Name{ get; set; }
public virtual List<User> Users { get; set; }
}
My problem is that when i use this code there's no relation many to many created. I've got a column name "Group_Id" in my table User and a column name "User_Id" in my table Group.
When I use my DbContext class to retrieve Data like this:
List groups = db.Groups.ToList();
The attribute "Users" of all my object in "groups" or set to null. So not load by the database.
Can someone explain me how to make this relation many to many work fine ?

If you are using Entity Framework, use the ObjectQuery<T>.Include method:
List groups = db.Groups.Include("Users").ToList()
A reference to the method on MSDN is here.

Related

Entity FrameWork , many to many realtion , reading data from navigation properties of Entity

I am working on asp.net web forms (C#) , with mysql and entity framework.
There in my database , a User table is with many to many relationship with permission table , and a third table UserPermission to establish this relation.
Following are the classes generated by entity framework data model.
public class User{
public int Serial { get; set; }
public string User_Name { get; set;
public virtual ICollection<UserPermission> UserPermission { get; set; }
}
public class Permission{
public int Serial { get; set; }
public string Name { get; set;
public virtual ICollection<UserPermission> UserPermission { get; set; }
}
public partial class UserPermission
{
public int Serial { get; set; }
public int USER_Sr { get; set; }
public int Permission_Sr { get; set; }
public virtual User User { get; set; }
public virtual Permission Permission { get; set; }
}
Now my problem is , that based on a user id , i want read the permissions it's associated with.
I want to have users with their assigned permissions , populate these into a gridview , where each row shows the record of a user , and there a combobox in that row contains the permissions , that user has gain.
Thank you very much for your help.
To get permissions assigned to a user
var userPermissions = MyContext.Users.Find(userId).Permissions();
Where MyContext is your DbContext and Users is defined as DbSet in this context. You didn't specify what you are using to display information, so I can't help you on that.

Entity Framework returns null for Include properties

I got 3 entities(tables) that have many to many connections:
public class AccUserRole
{
public long Id { get; set; }
public string RoleName { get; set; }
public List<AccAdGroup> Groups { get; set; }
public List<AccScreen> Screens { get; set; }
}
public class AccAdGroup
{
public long Id { get; set; }
public string AdIdent { get; set; }
public List<AccUserRole> Roles { get; set; }
}
public class AccScreen
{
public long Id { get; set; }
public string ScreenIdent { get; set; }
public List<AccUserRole> Roles { get; set; }
}
I wanted to get all Roles(including their screens and groups) that has at least one of specified list of groups(the groups of the current user). So I used this query:
List<AccUserRole> userRoles = (from ur in db.AccUserRoles.Include("Groups").Include("Screens")
from g in ur.Groups
where user.Groups.Contains(g.AdIdent)
select ur).ToList();
It gets the right roles, but the Groups and Screens properties are null. Looks like EF has a problem with using Include and second from.
Any help on how to include the properties or rewrite the query will be appreciated.
Eager Loading
The reason for this is that you have specified only one level of include, while your query is asking for something on the second level.
Your include lets you ask for ur.Groups and ur.Screens.
The next level is from g in ur.Groups, and you haven't included that level. (This is probably unexpected for you, since you already have asked for all AccUserRoles in the first part of the query.)
To make your query run, you could add another .include at the start, going two levels deep:
from ur in db.AccUserRoles
.Include("Groups")
.Include("Groups.Roles")
.Include("Screens")
If you need to go yet another level, you'd just add yet another include:
from ur in db.AccUserRoles
.Include("Groups")
.Include("Groups.Roles")
.Include("Groups.Roles.Groups")
.Include("Screens")
...etc.
This might become cumbersome if you have a whole lot of levels to nest, so an alternative would be to use Lazy Loading instead, as Praval 'Shaun' Tirubeni suggests, by adding the virtual keyword to the collections in your entities.
Move the include before ToList().
select ur).Include("Groups").Include("Screens").ToList();
The subselect can remove the Include effect.
If you are doing eager loading, the virtual keyword is not needed.
By adding virtual, you are using lazy loading, not eager loading.
Try adding the virtual key word to your class properties like so:
public class AccUserRole
{
public long Id { get; set; }
public string RoleName { get; set; }
public virtual List<AccAdGroup> Groups { get; set; }
public virtual List<AccScreen> Screens { get; set; }
}
public class AccAdGroup
{
public long Id { get; set; }
public string AdIdent { get; set; }
public virtual List<AccUserRole> Roles { get; set; }
}
public class AccScreen
{
public long Id { get; set; }
public string ScreenIdent { get; set; }
public virtual List<AccUserRole> Roles { get; set; }
}

How can I write a simple LINQ report for a many to many relationship?

My application uses ASP.NET Identity 2 with tables stored in SQL Server 2012 Relational tables. Here's a view of two classes that I would like to use for reporting:
public partial class AspNetUser
{
public AspNetUser()
{
this.AspNetRoles = new List<AspNetRole>();
}
public string Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<AspNetRole> AspNetRoles { get; set; }
}
public partial class AspNetRole
{
public AspNetRole()
{
this.AspNetUsers = new List<AspNetUser>();
}
public string Id { get; set; }
public string Name { get; set; }
public virtual ICollection<AspNetUser> AspNetUsers { get; set; }
}
I created the following LINQ to give me a report:
var users = await db.AspNetUsers
.Include(u => u.AspNetRoles)
.ToListAsync();
This is inside a WebAPI method and when I check the result it gives me a
result that I don't expect. What I see is an array of user objects and
then inside that an array of Roles. But inside the array of Roles is
another array of users!
Here's the output that I see with some fields ommitted to make it show
easily:
[{"id":"27cd003a-fb6a-4a2d-8df9-f502bc10a583"
"aspNetRoles":[
{
"id":"53724f55-af7a-448b-9ae2-1fe295b109fd",
"name":"Admin",
"aspNetUsers":[
{
"id":"527ddbd5-14d3-4fb9-a7ae-374e66f635d4",
"aspNetRoles":[]
},
{
"id":"e87c05bc-8305-45d0-ba07-3dd24438ba8b",
"aspNetRoles":[]
}
]},
{"id":"527ddbd5-14d3-4fb9-a7ae-374e66f635d4",
How can I change the LINq so that it gives me an array of AspNetUser objects and inside a simple array of AspNetRole objects? I also need the query to show me the users even if they have no roles. Something like this:
User1
Role1
Role2
User2
Role1
User3
User4
Role2
As you said, you have no control over the domain classes -- they belong to Microsoft. So, what you're really saying is that you don't want to expose your domain objects directly to the rest of the world (google that and you'll find all sorts of people with opinions on that subject). So, one option is to define a set of classes which are the ones you want to expose (data exchange classes). Something like:
public class User
{
public string Id { get; set; }
public string UserName { get; set; }
public virtual List<Role> Roles { get; set; }
}
public class Role
{
public string Id { get; set; }
public string Name { get; set; }
}
Then, when you get your domain objects back, map them into your data exchange objects for serialization and exposure to the rest of the world.

Using ICollection with MVC & Entity framework

Hi I'm learning to use MVc using Code first method. I'm having trouble understanding the use of ICollection<> with my setup. I have two classes/entitys: 'Restaurant' & 'RestaurantReview' as below:
public class Restaurant
{
public int Id { get; set; }
public string Name { get; set; }
public string City { get; set; }
public string Country { get; set; }
public ICollection<RestaurantReview> Reviews { get; set; }
}
and.....
public class RestaurantReview
{
public int Id { get; set; }
public int Rating { get; set; }
public string Body { get; set; }
public string ReviewerName { get; set; }
public int RestaurantId { get; set; }
}
now whats confusing me is the last property of the Restaurant class. Why is it of type 'ICollection' and uses my RestaurantReview class as a parameter, what does it do, hope I
have made myself clear
It's a definition of one to many relationship.
With that property (sometimes called Navigation Property) Entity Framework will be able to connect Review with Restaurant it was written about. It will also allow you to get Review entities for given Restaurant entity really easily.
You can also remove public int RestaurantId { get; set; } from RestaurantReview class - That column is gonna be generated automatically by EF because of ICollection<RestaurantReview> in Restaurant class.

How to add two different properties of the same class to one POCO class?

I have a class that has two references to one class (User):
public class Xpto {
public string Username { get; set; }
public virtual User User { get; set; }
public string Username2 { get; set; }
public virtual User User2 { get; set; }
}
The thing is EF only creates references to the first key (Username). That way User and User2 have Username as key and not what I intended...
I found this to be the answer:
nHibernate, mapping two properties to the same class
But I wouldn't know how to apply this to my scenario.
Thanks.
EDIT: Folks, nevermind... I guess I should've looked a little further.
The answer is here:
How do I create a POCO object that has 2 references to another class
The standard is <property_name><key_name>
So the correct way would be UserUsername and User2Username
Thanks.
Consider using the ForeignKeyAttribute instead, then you can select the names you like for your key attributes.
public class Xpto {
[ForeignKey("User")]
public string Username { get; set; }
public virtual User User { get; set; }
[ForeignKey("User2")]
public string Username2 { get; set; }
public virtual User User2 { get; set; }
}

Categories