EF Code First, specify relationship table? - ef-code-first

Classes:
public class Action
{
public long ID{ get; set; }
public string Name{ get; set; }
public virtual ICollection<User> Users{ get; set; }
}
public class User
{
public long ID{ get; set; }
public string Name{ get; set; }
public virtual ICollection<Action> Actions{ get; set; }
}
Tables:
Actions
| ID | Name |
Users
|ID | Name |
ActionUsers
| Action_ID | User_ID |
Note that Action and User has many-to-many relationship.
Assuming that EF can map the above example successfully (I simplified my original code), then I decided to do the following:
rename the table ActionUsers to Permissions
rename member of Action class, Users to PermittedUsers
rename member of User class, Actions to Permissions
How would I tell the EF to use Permissions as relationship table instead of ActionUsers when mapping User.Permissions and Action.PermittedUsers? Can I achieve the desired configuration without using Fluent API?

Using Fluent API :
public class Action
{
public long ID{ get; set; }
public string Name{ get; set; }
public virtual ICollection<User> PermittedUsers{ get; set; }
}
public class User
{
public long ID{ get; set; }
public string Name{ get; set; }
public virtual ICollection<Action> Permissions{ get; set; }
}
you can simply override OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Action>().
HasMany(c => c.PermittedUsers).
WithMany(p => p.Permissions).
Map(
m =>
{
m.MapLeftKey("Action_ID");
m.MapRightKey("User_ID");
m.ToTable("Permissions");
});
}
if you want to have your own naming convention you can define Permission Table as a class :
public class Permissions
{
[ForeignKey("Action")]
public int Action_ID { get; set; }
public Action Action { get; set; }
[ForeignKey("User")]
public int User_ID { get; set; }
public User User { get; set; }
}
// context
public class MyContext : DbContext
{
public DbSet<Action> Actions { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<Permissions> Permissions { get; set; }
}

Related

How can I set up two navigation properties of the same type in Entity Framework without use Fluent API

i'm trying create DB using codefirst. i want to create two ForeingKey from same table. But when i set up two navigation properties of the same type, get error like :
The foreign key name 'FollowedUser' was not found on the dependent type Models.UserUserWatchListItem'. The Name value should be a comma separated list of foreign key property names.
public class UserUserWatchListItem
{
public int Id { get; set; }
[Key,ForeignKey("FollowedUser")]
public virtual User FollowedUser { get; set; }
public int FollowedUserId { get; set; }
[Key,ForeignKey("FolloweeUser")]
public int FolloweeUserId { get; set; }
public virtual User FolloweeUser { get; set; }
}
Use this :
public class UserUserWatchListItem
{
public int Id { get; set; }
public int FollowedUserId { get; set; }
public int FolloweeUserId { get; set; }
[ForeignKey("FollowedUser")]
[InverseProperty("FollowedUsers")]
public virtual User FollowedUser { get; set; }
[ForeignKey("FolloweeUser")]
[InverseProperty("FolloweeUsers")]
public virtual User FolloweeUser { get; set; }
}
public class User
{
...
[InverseProperty("FollowedUser")]
public virtual ICollection<UserUserWatchListItem> FollowedUsers { get; set; }
[InverseProperty("FolloweeUser")]
public virtual ICollection<UserUserWatchListItem> FolloweeUsers { get; set; }
}

Table with foreign key of same entity type

Hi I've been searching around for like 40 minutes now trying to figure out how to do this and I'm not having any luck. I'm creating a forum app with ASP.NET. MVC5, and EF6. My app contains a Comment model; this is where I started running into problems. I want threads to be able to have comments(this was easy) and I also want comments to have comments(This is my problem).
Here is how my model is defined:
namespace Forum.Models
{
public class Comment
{
[Key]
public int Id {get; set;}
[DisplayFormat(DataFormatString = "{0:d/M/yyyy HH:mm:ss}",
ApplyFormatInEditMode = true)]
public DateTime TimeStamp { get; set; }
public string Content { get; set; }
public String UserId { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
[ForeignKey("ParentComment")]
public int ParentCommentId { get; set; }
public virtual Comment ParentComment { get; set; }
public int ThreadId { get; set; }
public virtual Thread Thread {get; set;}
}
}
This is the error I get when I try to update this table:
Cannot insert explicit value for identity column in table 'Comments' when IDENTITY_INSERT is set to OFF.
Any help would be greatly appreciated.
I agree with #Slauma, you need to change ParentCommentId to int? type. Also if you want to use ForeignKeyAttribute, you need to assign it to navagation property, like below:
public int? ParentCommentId { get; set; }
[ForeignKey("ParentCommentId")]
public virtual Comment ParentComment { get; set; }
Below is an example, I'm using fluent API to configure the relationships.
Coment Model class:
public class Comment
{
[Key][DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[DisplayFormat(DataFormatString = "{0:d/M/yyyy HH:mm:ss}",ApplyFormatInEditMode = true)]
public DateTime TimeStamp { get; set; }
public string Content { get; set; }
public String UserId { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public int? ParentCommentId { get; set; }
public virtual Comment ParentComment { get; set; }
public int ThreadId { get; set; }
public virtual Thread Thread { get; set; }
}
DbContext class:
public class YourDbContext : DbContext
{
public DbSet<Comment> Comments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Comment>()
.HasOptional(c => c.ParentComment )
.WithMany(c => c.Comments)
.HasForeignKey(c => c.ParentCommentId );
}
}

convert ASp.net membership provider classes to code first entities in entity framework

I am using entity frame work code first approach to design my web application in asp.net 4.5.
I have converted all asp.net membership related tables in code first entities as below
public class Application
{
public string ApplicationName { get; set; }
[Key]
public System.Guid ApplicationId { get; set; }
public string Description { get; set; }
public virtual ICollection<Membership> Memberships { get; set; }
public virtual ICollection<Role> Roles { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class Membership
{
[ForeignKey("Application")]
public System.Guid ApplicationId { get; set; }
[Key, ForeignKey("User")]
public System.Guid UserId { get; set; }
public string Password { get; set; }
public int PasswordFormat { get; set; }
public string PasswordSalt { get; set; }
public string Email { get; set; }
public string PasswordQuestion { get; set; }
public string PasswordAnswer { get; set; }
public bool IsApproved { get; set; }
public bool IsLockedOut { get; set; }
public System.DateTime CreateDate { get; set; }
public System.DateTime LastLoginDate { get; set; }
public System.DateTime LastPasswordChangedDate { get; set; }
public System.DateTime LastLockoutDate { get; set; }
public int FailedPasswordAttemptCount { get; set; }
public System.DateTime FailedPasswordAttemptWindowStart { get; set; }
public int FailedPasswordAnswerAttemptCount { get; set; }
public System.DateTime FailedPasswordAnswerAttemptWindowsStart { get; set; }
public string Comment { get; set; }
public virtual Application Application { get; set; }
public virtual User User { get; set; }
}
public class Profile
{
[Key, ForeignKey("User")]
public System.Guid UserId { get; set; }
public string PropertyNames { get; set; }
public string PropertyValueStrings { get; set; }
public byte[] PropertyValueBinary { get; set; }
public System.DateTime LastUpdatedDate { get; set; }
public virtual User User { get; set; }
}
public class Role
{
[ForeignKey("Application")]
public System.Guid ApplicationId { get; set; }
[Key]
public System.Guid RoleId { get; set; }
public string RoleName { get; set; }
public string Description { get; set; }
public virtual Application Application { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class User
{
[ForeignKey("Application")]
public System.Guid ApplicationId { get; set; }
[Key]
public System.Guid UserId { get; set; }
public string UserName { get; set; }
public bool IsAnonymous { get; set; }
public System.DateTime LastActivityDate { get; set; }
public virtual Application Application { get; set; }
public virtual Membership Membership { get; set; }
public virtual Profile Profile { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
My Datacontext and initalizer classes are as below
public class TrainningInitializer : DropCreateDatabaseIfModelChanges<TrainningContext>
{
protected override void Seed(TrainningContext context)
{
}
}
public partial class TrainningContext : DbContext
{
public TrainningContext()
: base("name=TrainningContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
public DbSet<Application> Applications { get; set; }
public DbSet<Membership> Memberships { get; set; }
public DbSet<Profile> Profiles { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<User> Users { get; set; }
}
I am setting Initializer as below in application_start event inside Global.asax.
Database.SetInitializer<TrainningContext>(new TrainningInitializer());
But when I am running my application I am getting below error
Introducing FOREIGN KEY constraint 'FK_dbo.RoleUsers_dbo.Users_User_UserId' on table 'RoleUsers' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.
Can someone please help in correcting my entity classes.
When you delete an Application entity this delete cascades to the Roles and the Users and then from Roles to the RoleUsers join table and from the Users to RoleUsers. Those two delete paths from Application to RoleUsers table are the "multiple cascade paths" the exception is talking about. They are not allowed in SQL Server.
Cascading delete is enabled by default for the Roles and Users relationships of Application because the foreign key ApplicationId in these two tables is not nullable, hence the relationship is required. Required relationships have cascading delete turned on by default.
But you can turn it off with Fluent API:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Application>()
.HasMany(a => a.Roles)
.WithRequired(r => r.Application)
.HasForeignKey(r => r.ApplicationId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Application>()
.HasMany(a => a.Users)
.WithRequired(u => u.Application)
.HasForeignKey(u => u.ApplicationId)
.WillCascadeOnDelete(false);
}
(It's probably sufficient for one of the two.)
If you would delete an Application entity now, you need to delete the related Roles and Users as well. The database won't do that automatically for you anymore.

multiple relationships with same table by Dataannotation with code first

I have the following model:
public class Member
{
public int Id { get; set; }
public string Username { get; set; }
public int MainEmailId { get; set; }
public virtual Email MainEmail { get; set; }
public virtual ICollection<Email> MemberEmails { get; set; }
}
public partial class Email
{
public Int32 Id { get; set; }
public String Email { get; set; }
public int MemberId { get; set; }
public virtual Member Member { get; set; }
}
As you can see I wish to create a:
one-to-one relation from the Member to MemberEmail (the main email address)
one-to-many relation from Member to MemberEmail
I know how to do this with Code First Fluent API. However I need to do it with DataAnnotations only. Is this possible?
Thanks a lot.

How to configure multiple Foreignkey referancing to same table from main table using EF Code First Fluient API

I have to design a web application using existing database of desktop application. As per existing database i have below class
public class Company
{
#region Primitive Properties
public virtual int CompanyID { get; set; }
public virtual string CompanyName { get; set; }
public virtual bool IsCustomer { get; set; }
public virtual string CustomerCode { get; set; }
public virtual bool IsPotentialCustomer { get; set; }
public virtual bool IsSupplier { get; set; }
public virtual string SupplierCode { get; set; }
public virtual bool IsPotentialSupplier { get; set; }
public CompanyCategoryCodes CustomerCategoryCode { get; set; }
public CompanyCategoryCodes SupplierCategoryCode { get; set; }
public CountryCode CountryCode { get; set; }
}
public class CompanyCategoryCodes
{
public virtual int CategoryID { get; set; }
public virtual string CategoryCodes { get; set; }
public virtual bool PotentialCustomer { get; set; }
public virtual bool PotentialSupplier { get; set; }
public virtual System.DateTime LastModifiedDate { get; set; }
public virtual bool Manufacturer { get; set; }
}
public class CountryCode
{
public virtual int CountryCodeID { get; set; }
public virtual string Code { get; set; }
public virtual string Description { get; set; }
public virtual bool DefaultCode { get; set; }
public virtual bool EECVATApplies { get; set; }
public virtual System.DateTime LastModifiedDate { get; set; }
public virtual bool FixedAddressFormat { get; set; }
}
EF Code first default framework is creating Foreignkey with name "CustomerCategoryCode_CategoryID" , "SupplierCategoryCode_CategoryID", "CountryCode_CountryCodeID". I want this Foreignkey name to be consistance with my old database tables e.g. "CustomerCategoryCodeID", "SupplierCategoryCodeID", "CountryCodeID". How can i do it using EF Code First Fluient API. I try to do it using Fluient API Mapping but i got error for "SupplierCategoryCode_CategoryCodeID" as "CustomerCategoryCode_CategoryID" is also locating to same table "CompanyCategoryCode". Also if is there any option available using Data Annotation then also let me know how to achieve this.
You must manually remap each navigation property to define its key. Something like:
modelBuilder.Entity<Company>()
.HasRequired(c => c.CountryCode)
.WithMany()
.HasForeignKey("CountryCodeID");
modelBuilder.Entity<Company>()
.HasMany(c => c.CustomerCategoryCode)
.WithOptional()
.HasForeignKey("CustomerCategoryCodeID")
.WillCascadeOnDelete(false);
modelBuilder.Entity<Company>()
.HasMany(c => c.SupplierCategoryCode)
.WithOptional()
.HasForeignKey("SupplierCategoryCodeID")
.WillCascadeOnDelete(false);
It is not possible with data annotations unless you define navigation property and foreign key property in each dependent entity like:
public class Company
{
...
[ForeignKey("CountryCode")]
public virtual int CountryCodeID { get; set; }
public CountryCode CountryCode { get; set; }
}
Inside your context class, you will need to override OnModelCreating and map the keys, it should look something like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Company>()
.HasRequired(c => c.CustomerCategoryCode)
.WithMany()
.Map(mc => mc.MapKey("CustomerCategoryCodeID"));
}

Resources