I have 3 Entities that have many combinations on my site.
I would to create the follow hierarchy:
Each User have collection of UserRoles.
Each UserRole have a fixed collection of PermissionRecords
Each PermissionRecord have a fild of PermissionRecordPrivileges that varies from user to user.
I would like to get the user's privileges (getting the permissionRecord and UserRole collection is quite trivial).
As I understand, I need to create a table that merges the following data:
UserId, PermissionRecordId, PermissionPrivilegesId (3 Foreign keys that create primary key)
How can I do this using EF 5 ( or earlier)?
The code:
public class BaseEntity
{
public int Id { get; set; }
}
public class User:BaseEntity
{
public virtual ICollection<UserRole> UserRoles{get;set;}
}
public class UserRole:BaseEntity
{
public ICollection<PermissionRecord> PermissionRecords { get; set; }
}
public class PermissionRecord : BaseEntity
{
public PermissionRecordPrivileges Privileges { get; set; }
}
public class PermissionRecordPrivileges : BaseEntity
{
public bool Create { get; set; }
public bool Read { get; set; }
public bool Update { get; set; }
public bool Delete { get; set; }
}
Your terminology "create a table" is a bit confusing. A table is a database object. I assume you mean a data structure client-side. To collect a User's privileges you can do:
var privileges = (from u in context.Users
from ur in u.UserRoles
from pr in ur.PermissionRecords
where u.UserId = id
select ur.Privileges).Distinct();
where id is a variable containing a User's id.
You have to create your entity model classes like below.
Note : Need to maintain Conventions(naming and singular/plural) when you use Entity framework Code First.Like below :
Entity Models
public class UserRole
{
public int Id { get; set; }
public virtual ICollection<PermissionRecord> PermissionRecords { get; set; }
}
public class PermissionRecord
{
public int Id { get; set; }
public virtual PermissionRecordPrivilege PermissionRecordPrivilege { get; set; }
}
public class PermissionRecordPrivilege
{
public int Id { get; set; }
public bool Create { get; set; }
public bool Read { get; set; }
public bool Update { get; set; }
public bool Delete { get; set; }
}
Your Tables Should like below :
Tables
PermissionRecordPrivileges
Id int NotNull
Create bit AllowNull
Read bit AllowNull
Update bit AllowNull
Delete bit AllowNull
PermissionRecords
Id int NotNull
PermissionRecordPrivilege_Id int NotNull
UserRole_Id int NotNull
UserRoles
Id int NotNull
I hope this will help to you.Good Luck.
Related
I have two entities in my EF Code first and they have a foreign key relationship.
public class Condition
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
}
public class ConditionGroup
{
public int Id { get; set; }
public Condition Condition { get; set; }
public int ConditionId { get; set; }
}
This is my entity map:
public class ConditionGroupMap : EntityTypeConfiguration<ConditionGroup>
{
public ConditionGroupMap()
{
this.ToTable("ConditionGroup");
this.HasKey(cg => cg.Id);
this.HasRequired(cg => cg.Condition).WithMany(c => c.ConditionGroups).HasForeignKey(cg => cg.ConditionId).WillCascadeOnDelete(true);
}
}
EF will create the a foreign key object in the database with the following Name:
ConditionGroup_Condition.
The problem is that this name collides with another object in the database for reasons which are beyond the scope of this question. So I would like to ask if there is a way to change this name?
Instead of doing it with the Fluent API, you could do it with Data Annotations:
public class ConditionGroup
{
public int Id { get; set; }
[ForeignKey("Condition")]
public int ConditionId { get; set; }
public Condition Condition { get; set; }
}
If that doesn't end up working you could always use the Column() Attribute to give it whatever column name you wished.
I have two database tables:
Customers
CustomerId (PK)
Name
...
CustomerSettings
CustomerId (PK)
Setting1
Setting2
...
Is it possible to have these classes using code-first? If so, what is the fluent mapping?
public class Customer
{
public int CustomerId { get; set; }
public int Name { get; set; }
public CustomerSetting CustomerSetting { get; set; }
}
public class CustomerSetting
{
public int CustomerId { get; set; }
public int Setting1 { get; set; }
public int Setting2 { get; set; }
public Customer Customer { get; set; }
}
I personally don't like one-to-one tables. After all, why not just add the setting columns to the customer table? Unfortunately, this is what I need to develop against. I can't figure the correct code-first mappings for such a scenario. Thanks for your help.
If you are going for code first and want to have both Customer And CustomerSettings classes,
but only a single table for both, as your post suggests ,
I would use complex types.
see a good example here:
http://weblogs.asp.net/manavi/archive/2010/12/11/entity-association-mapping-with-code-first-part-1-one-to-one-associations.aspx
So , your object model should look like this (I've not tested it):
public class Customer
{
public Customer()
{
CustomerSetting= new CustomerSetting();
}
public int CustomerId { get; set; }
public int Name { get; set; }
public CustomerSetting CustomerSetting { get; set; }
}
[ComplexType]
public class CustomerSetting
{
public int CustomerId { get; set; }
public int Setting1 { get; set; }
public int Setting2 { get; set; }
public Customer Customer { get; set; }
}
Your model classess are correct, if you want you can add this to your model builder to specify which table is the "main one":
.Entity<Customer>()
.HasOptional(c => c.CustomerSetting)
.WithRequired(u => u.Customer);
For all classes in my Entity I have a base class named CommonFields
public class CommonFields
{
public int Status { get; set; }
public DateTime CreatedOn { get; set; }
public int CreaedBy { get; set; }
public DateTime ModifiedOn { get; set; }
public int ModifiedBy { get; set; }
}
And, for eg. I have two classes like
public class Employee : CommonFields
{
public int Id { get; set; }
public string Name { get; set; }
//Other properties
}
public class User : CommonFields
{
public int Id { get; set; }
public string Name { get; set; }
//Other properties
}
How can I set relation from CreatedBy & ModifiedBy to User table. I just need only one directional mapping (I don't want FK to be created in my User Table).
I need to get User information when I write objEmployee.CreatedUser
Thanks.
I have a code first class
public class test
{
public int ID { get; set; }
public int ManagerID { get; set; }
[ForeignKey("ManagerID")]
public Person Manager { get; set; }
}
In databas table is created correctly, however then I try to access
pTest.Manager it returns null
In database table test field ManagerID has correct id value for person.
Make the Manager property virtual so that EF can lazy load it or use eager loading(using the Include method).
public class test
{
public int ID { get; set; }
public int ManagerID { get; set; }
[ForeignKey("ManagerID")]
public virtual Person Manager { get; set; }
}
I have two tables (Table A, Table B) joined with a join table (TableAB) with 3 payload columns. By Payload I mean columns apart from Id, TableAId, and TableBId.
I can insert into all tables successfully, but I need to insert data into one of the payload columns on Insert. I'm using EF 4.3, Fluent API. Can anyone help? Thanks in advance.
public class Organisation : EntityBase<int>, IAggregateRoot
{
public string Name { get; set; }
public string Url { get; set; }
public int CountryId { get; set; }
public int? OwnershipTypeId { get; set; }
public int OrganisationStatusId { get; set; }
public virtual ICollection<Feature> Features { get; set; }
public virtual ICollection<OrganisationType> OrganisationTypes { get; set; }
public virtual ICollection<PricePlan> PricePlans { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class User: EntityBase<Guid>, IAggregateRoot
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string JobTitle { get; set; }
public int? PhoneCallingCodeId { get; set; }
public int? PhoneAreaCode{ get; set; }
public string PhoneLocal { get; set; }
public int? MobileCallingCodeId { get; set; }
public int? MobileAreaCode { get; set; }
public string MobileLocal { get; set; }
public virtual ICollection<Organisation.Organisation> Organisations { get; set; }
}
public class OrganisationUser : EntityBase<int>, IAggregateRoot
{
public DateTime StartDate { get; set; }
public DateTime? EndDate { get; set; }
public int OrganisationRoleId {get; set;}//Foreign Key - have tried leaving it out, tried it as public virtual Organisation Organisation {get;set;
public bool IsApproved { get; set; }
}
public class SDContext : DbContext
{
public ObjectContext Core
{
get
{
return (this as IObjectContextAdapter).ObjectContext;
}
}
public IDbSet<User> User { get; set; }
public IDbSet<Organisation> Organisation { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Organisation>().HasMany(u => u.Users).WithMany(o => o.Organisations).Map(m =>
{
m.MapLeftKey("OrganisationId");
m.MapRightKey("UserId");
m.ToTable("OrganisationUser");
});
//I have tried specifically defining the foreign key in fluent, but I really need to understand how I can add the payload properties once I access and edit them.
Your mapping is not correct for your purpose. If you want to treat OrganisationUser as an intermediate entity between Organisation and User you must create relationships between Organisation and OrganisationUser and between User and OrganisationUser, not directly between Organisation and User.
Because of the intermediate entity which contains its own scalar properties you cannot create a many-to-many mapping. EF does not support many-to-many relationships with "payload". You need two one-to-many relationships:
public class Organisation : EntityBase<int>, IAggregateRoot
{
// ...
// this replaces the Users collection
public virtual ICollection<OrganisationUser> OrganisationUsers { get; set; }
}
public class User : EntityBase<Guid>, IAggregateRoot
{
// ...
// this replaces the Organisations collection
public virtual ICollection<OrganisationUser> OrganisationUsers { get; set; }
}
public class OrganisationUser : EntityBase<int>, IAggregateRoot
{
public int OrganisationId { get; set; }
public Organisation Organisation { get; set; }
public Guid UserId { get; set; }
public User User { get; set; }
// ... "payload" properties ...
}
In Fluent API you must replace the many-to-many mapping by the following:
modelBuilder.Entity<Organisation>()
.HasMany(o => o.OrganisationUsers)
.WithRequired(ou => ou.Organisation)
.HasForeignKey(ou => ou.OrganisationId);
modelBuilder.Entity<User>()
.HasMany(u => u.OrganisationUsers)
.WithRequired(ou => ou.User)
.HasForeignKey(ou => ou.UserId);
Your derived DbContext may also contain a separate set for the OrganisationUser entity:
public IDbSet<OrganisationUser> OrganisationUsers { get; set; }
It's obvious now how you write something into the intermediate table:
var newOrganisationUser = new OrganisastionUser
{
OrganisationId = 5,
UserId = 8,
SomePayLoadProperty = someValue,
// ...
};
context.OrganisastionUsers.Add(newOrganisastionUser);
context.SaveChanges();
If you want to make sure that each pair of OrganisationId and UserId can only exist once in the link table, it would be better to make a composite primary key of those two columns to ensure uniqueness in the database instead of using a separate Id. In Fluent API it would be:
modelBuilder.Entity<OrganisationUser>()
.HasKey(ou => new { ou.OrganisationId, ou.UserId });
More details about such a type of model and how to work with it is here:
Create code first, many to many, with additional fields in association table