Entity Framework, many-to-many relationship and querying - entity-framework-4.1

I have a many-to-many relationship between users and groups and I have a table which can contain a permission for a group. So the entities look something like:
public class Group
{
public int Id { get; set; }
public virtual ICollection Users { get; set; }
}
public class User
{
public int Id { get; set; }
public virtual ICollection Groups { get; set; }
}
public class Permission
{
public int Id { get; set; }
public virtual Group { get; set; }
public int Value { get; set; }
}
I am wondering how I find out the permissions that are applicable to a user (where the applicable ones are for any groups to which the user belongs).
In the database there will be a mapping table, called UserGroups. If I had access to that, the LINQ query would look something like:
var permissions =
from p in MyContext.Permissions
join m in this.DbContext.UserGroups on p.GroupId equals m.GroupId
where m.UserId.Equals(theUserId)
select g;
However (see my related question), since I don't have access to the mapping table, I am not sure the best way to find the applicable permissions. What is the best way to do it?
Thanks for your help,
Eric

Try
var permissions = MyContext.Permissions
.Where(p => p.Group.Users.Any(u => u.Id == theUserId));

Related

How can we manage the Relation between two table in POCO Entity?

I am a newbie to POCO.I have two tables like tb1 and tb2.Suppose we have a PK and FK relation between these tables.When it come to POCO CF how can we manage this relations?I have a done a sample by following a article.
public abstract class Person
{
public string Name { get; set; }
public int DepartmentId { get; set; }
public virtual Department Department { get; set; }
}
public class Collaborator : Person
{
public int CollaboratorId { get; set; }
public string ManagerCode { get; set; }
public virtual Manager Manager { get; set; }
}
Why they have used the abstract and virtual keywords? Can any one explain me the how can we manage the relations?
I assume you are using a model-first approach. You will want to use the Fluent API to define the relationships. Here is a good article on how to do this.

Mapping many to many relationship

I am have some trouble getting Entity Framework to handle a many to many relationship in my data schema. Here is my model:
public class User
{
public int UserId { get; set; }
public int Username { get; set; }
public IEnumerable<Customer> Customers { get; set; }
...
}
public class Customer
{
public int CustomerId { get; set; }
...
}
public class CustomerUser
{
public int CustomerUserId { get; set; }
public int CustomerId { get; set; }
public int UserId { get; set; }
public DateTime CreatedTimestamp { get; set; }
...
}
Here is the mapping:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>().HasKey(u => u.UserId).ToTable("Users");
modelBuilder.Entity<Customer>().HasKey(c => c.CustomerId).ToTable("Customer");
modelBuilder.Entity<CustomerUsers>().HasKey(cu => cu.CustomerUserId).ToTable("CustomerUsers");
modelBuilder.Entity<CustomerUsers>()
.HasRequired(cu => cu.User)
.WithRequiredDependent()
.Map(m =>
{
m.ToTable("Users");
m.MapKey("CustomerUsers.UserId");
});
}
My database has a Users, Customers, and CustomerUsers table with columns that match the model.
I am trying to execute the following query:
result = (from u in context.Users
join customerUsers in context.CustomerUsers on u.UserId equals customerUsers.User.UserId
join customers in context.Customers on customerUsers.CustomerId equals customers.CustomerId into ps
select new
{
User = u,
Customers = ps
}).ToList().Select(r => { r.User.Customers = r.Customers.ToList(); return r.User; });
When I run the code, I get the following error:
The Column 'CustomerUserId' specified as part of this MSL does not exist in MetadataWorkspace
Can anyone see what is wrong with my approach?
Thanks!
I should note that I am intentionally trying to not include a reference to the CustomerUsers table from either the Customer or User class. The majority of the time, the payload of the CustomerUsers table is not important, only which customers are associated to which users. There are some reporting scenarios where the additional information in the join table is necessary, but since this is not the typical situation, I would like to avoid cluttering up the models by having this additional indirection.
Instead of trying to map this as many to many, map it as two one to many relationships. See the discussion of many to many join tables with payload in Many-to-Many Relationships in this tutorial:
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/creating-a-more-complex-data-model-for-an-asp-net-mvc-application
For your model you will need probably two one-to-many relationships and the following navigation properties:
public class User
{
public int UserId { get; set; }
public int Username { get; set; }
// ...
public ICollection<CustomerUser> CustomerUsers { get; set; }
}
public class Customer
{
public int CustomerId { get; set; }
//...
public ICollection<CustomerUser> CustomerUsers { get; set; }
}
public class CustomerUser
{
public int CustomerUserId { get; set; }
public int CustomerId { get; set; }
public int UserId { get; set; }
public DateTime CreatedTimestamp { get; set; }
//...
public User User { get; set; }
public Customer Customer { get; set; }
}
And the following mapping:
modelBuilder.Entity<CustomerUser>()
.HasRequired(cu => cu.User)
.WithMany(u => u.CustomerUsers)
.HasForeignKey(cu => cu.UserId);
modelBuilder.Entity<CustomerUser>()
.HasRequired(cu => cu.Customer)
.WithMany(c => c.CustomerUsers)
.HasForeignKey(cu => cu.CustomerId);

How to use properly the ChangeTracker.Entries<Entity> in Entity Framework Code First

this is my simple DbContext inheriting class:
public class School : DbContext
{
public DbSet<Activity> Activity { get; set; }
public DbSet<Student> Student { get; set; }
public override int SaveChanges()
{
string s = string.Empty;
foreach (var entry in ChangeTracker.Entries<Activity>().Where(a => a.State != EntityState.Unchanged))
s = entry.State.ToString();
foreach (var entry in ChangeTracker.Entries<Student>().Where(a => a.State != EntityState.Unchanged))
s = entry.State.ToString();
return base.SaveChanges();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Configuration.ValidateOnSaveEnabled = false;
Configuration.LazyLoadingEnabled = true;
base.OnModelCreating(modelBuilder);
}
}
these are my entites:
public class Student
{
public int id { get; set; }
public string Name { get; set; }
public string Roll { get; set; }
//naviagtional property
public virtual IList<Activity> Activities { get; set; }
}
public class Activity
{
public int id { get; set; }
public double Maths { get; set; }
public double Science { get; set; }
public double History { get; set; }
//navigational property
public virtual Student Student { get; set; }
}
somewhere in my code i do this:
int studentId = Convert.ToInt32(Request.Form["Student.id"]);
Activity activity = dbContext.Activity.Where(e => e.Student.id == studentId).Single();
activity.Student.Name = Request.Form["Student.Name"];
activity.Student.Roll = Request.Form["Student.Roll"];
activity.Maths = Convert.ToDouble(Request.Form["Maths"]);
activity.Science = Convert.ToDouble(Request.Form["Science"]);
dbContext.SaveChanges();
Everything's normal and fine and works as it should. My question is, by updating activity.Student.Name, how can I detect change in Activity entity and not in Student entity? Is there any support in Entity Framework to detect changes in the parent table (and not in the slave table, where actual change goes though).??
Please help, it will save me a lot of time..
Even though this is an older question I thought I would give an answer anyway. NO you cannot.
The reasoning behind this is that you the programmer, as far as the code example goes is aware what is happening and could act on that (before doing the SaveChanges) to make sure whatever you want to happen is going to happen.
The Student you are changing might also be part of other entities, so would you also want those entities to be notified. An automatic behavior as you suggest would result in very complex notifications begin sent through the model which is (in most cases undesirable).
As #Ladislav Mrnka also indicated youy did not change the activity, but a Student involved in the activity. If the student relation is more than a simple lookup perhaps the model should be changed. Form the sample code given it is hard to see "why" you would need to detect changes made "through" other entities

EF 4.1 CF Fluent API mapping problemo

I'm new to the fluent API. I have a legacy database which I can't alter at the moment. Simply, this is what I need to achieve:
public class ItemCategory
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Item> Items { get; set; }
}
public class Item
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<ItemCategory> ItemCategories { get; set; }
public virtual ICollection<Item> RelatedItems { get; set; }
}
Items can be in many categories, RelatedItems can be in different categories to the current Item (which may not have any related items), the existing join tables look like this:
ItemCategoriesItems (ID,ItemCategoryID,ItemID)
RelatedItemCategoriesItems (ID,ItemCategoriesItemsID,RelatedItemCategoriesItemsID)
Hopefully it's obvious that the related items join table above contains 2 foreign keys to the item categories join table - one pointing to the current item and the other to the related item. Currently my onModelCreating code has:
modelBuilder.Entity<ItemCategory>()
.HasMany(c => c.Items)
.WithMany(set => set.ItemCategories)
.Map(mc =>
{
mc.ToTable("ItemCategoriesItems","testdb");
mc.MapLeftKey("ItemCategoryID");
mc.MapRightKey("ItemID");
});
... which gets the categories/items working but I'm stuck on how to get the RelatedItems.
Any help greatly appreciated!

EF 4 Code First and M2M2M

Using Entity Framework 4 and code first how would I create a model that supports this scenario:
In an application, there are users, each user belongs to one or more groups and for each group the user can have one or more roles.
Example:
I would like to be able to say, "give me Lisa", and the response returns a user object for lisa, with the groups she belongs to. For each group there is a list property with all the roles she has for that particular group
Can anyone help me model this using code first, any help/code samples, would be great!
/Best regards Vinblad
Edit: Here is new model for your requirement.
public class User
{
public virtual int Id { get; set; }
public virtual ICollection<UserPermission> Permissions { get; set; }
}
// Permission is extended junction table to model M:N between
// User and Group but in addition it contains relation to Roles.
// The ony disadvantage is that this model doesn't control that
// role in the collection is also the role related to group. You
// must either enforce it in application logic or create some additional
// database construct to check it.
public class UserPermission
{
public virtual int UserId { get; set; }
public virtual int GroupId { get; set; }
public virtual Group Group { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
public class Group
{
public virtual int Id { get; set; }
public virtual ICollection<UserPermission> UserPermissions { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
public class Role
{
public virtual int Id { get; set; }
public virtual ICollection<Group> Groups { get; set; }
public virtual ICollection<UserPermission> UserPermissions { get; set; }
}
public class Context : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<UserPermission> UserPermissions { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Permission has composite key
modelBuilder.Entity<UserPermission>()
.HasKey(p => new {p.UserId, p.GroupId});
// Permission doesn't have navigation property to user
modelBuilder.Entity<User>()
.HasMany(u => u.Permissions)
.WithRequired()
.HasForeignKey(p => p.UserId);
modelBuilder.Entity<Group>()
.HasMany(g => g.UserPermissions)
.WithRequired(p => p.Group)
.HasForeignKey(p => p.GroupId);
}
}
As described in code there is small disadvantage. You can avoid the disadvantage by enforcing data integrity in DB by additional FK which can't be modeled by code first. You can use custom initializer to add that FK:
public class CustomInitializer : DropCreateDatabaseIfModelChanges<Context>
{
protected override void Seed(Context context)
{
context.Database.ExecuteSqlCommand(
#"ALTER TABLE [dbo].[RoleUserPermissions]
WITH CHECK ADD CONSTRAINT [FK_RoleUserPermissions_RoleGroups]
FOREIGN KEY([Role_Id], [UserPermission_GroupId])
REFERENCES [dbo].[RoleGroups] ([Role_Id], [Group_Id])");
}
}
Just add this to your application initialization (only for debug - application should not be able to drop its database in release):
Database.SetInitializer(new CustomInitializer());