How to relate model property to same model in ASP.NET MVC? - asp.net-mvc

Sometimes, you want to store who registered or created a user account. It's either the user registered himself/herself or some other user account registered him, such as Admin accounts. So, the User table would something like:
public class User : Identity
{
public int Id { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string Name { get; set; }
// this is the part that I'd to relate to the same model
[ForeignKey("Id")]
public virtual User RegisteredBy { get; set; }
}
Using data annotations or Fluent API, how would you relate the User.RegisteredBy to User.Id?
Thanks in advance!

Something like in your class
public class User : Identity
{
public int Id { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string Name { get; set; }
// this is the self referential part
public int? RegisteredById { get; set; }
public virtual User RegisteredBy { get; set; }
public virtual ICollection<User> RegisteredUsers { get; set; }
}
and then in your DbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasOptional(x => x.RegisteredBy)
.WithMany(x => x.RegisteredUsers)
.HasForeignKey(x => x.RegisteredById);
}
This is untested, but I did something similar in a project a little while ago and it worked fine.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasOptional(c => c.RegisteredBy)
.WithMany()
.HasForeignKey(c => c.RegisteredById);
}
You can use the above Fluent Api code and your class should look like this
public class User : Identity
{
public int Id { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string Name { get; set; }
public int? RegisteredById { get; set; }
public User RegisteredBy { get; set; }
}

Related

Renaming default table names in asp.net mvc project without breaking foreign keys

I Created a new MVC 5 project whenever i try to rename the table names as described in this question
Change table names using the new Identity system
It always breaks the foreign key relationships in the Logins, Claims, Roles tables.
I have tried to override the OnModelCreating method but in vein here is my code
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//base.OnModelCreating(modelBuilder);
if (modelBuilder == null)
{
throw new ArgumentNullException("modelBuilder");
}
modelBuilder.Entity<App>().HasKey(m => new { m.AppId, m.FacebookId });
modelBuilder.Entity<IdentityUser>().ToTable("Admins");
modelBuilder.Entity<IdentityUser>().
Property(p => p.Id).HasColumnName("AdminId");
modelBuilder.Entity<Admin>()
.ToTable("Admins")
.Property(p => p.Id).HasColumnName("AdminId");
modelBuilder.Entity<IdentityUserLogin>().ToTable("Logins")
.HasKey(m => new { m.ProviderKey, m.UserId, m.LoginProvider })
.Property(m => m.UserId)
.HasColumnName("AdminId");
modelBuilder.Entity<IdentityUserRole>().ToTable("AdminRoles")
.HasKey(m => new { m.RoleId, m.UserId })
.Property(m => m.RoleId)
.HasColumnName("AdminRoleId");
modelBuilder.Entity<IdentityUserRole>().Property(m => m.UserId)
.HasColumnName("AdminId");
//modelBuilder.Entity<IdentityUserRole>().HasRequired(m => m.UserId);
//modelBuilder.Entity<IdentityUserLogin>().HasRequired(m => m.UserId);
modelBuilder.Entity<IdentityUserClaim>().ToTable("Claims")
.HasKey(m=> m.Id)
.Property(m => m.Id)
.HasColumnName("ClaimId");
modelBuilder.Entity<IdentityRole>().ToTable("Roles")
.HasKey(m => m.Id).Property(m => m.Id).HasColumnName("RoleId");
}
where the "Admin" Class is my name for the ApplicationUser Default class plus my own implementation of it
public class Admin : IdentityUser
{
[Required]
public virtual List<App> Apps { get; set; }
public bool? IsPremium { get; set; }
[DataType(DataType.Date)]
public DateTime? LastPublishDateTime { get; set; }
}
here are my other domain classes
public class App
{
[Key]
[Column("AppId", Order = 1)]
public virtual int AppId { get; set; }
[Required]
[Key]
[Column("FacebookId", Order = 2)]
public virtual string FacebookId { get; set; }
[Required]
public virtual string Secret { get; set; }
public virtual List<User> Users { get; set; }
public virtual List<Post> Posts { get; set; }
[Required]
public virtual Admin Admin { get; set; }
}
public class Post
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual int PostId { get; set; }
public virtual string Title { get; set; }
public virtual string Content { get; set; }
public virtual string Link { get; set; }
public virtual string Image { get; set; }
public virtual bool IsSpecial { get; set; }
[Required]
public virtual App App { get; set; }
[Required]
public virtual DateTime? PublishDate { get; set; }
}
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual int UserId { get; set; }
[MaxLength(500)]
public virtual string FacebookId { get; set; }
[MaxLength(500)]
public virtual string Token { get; set; }
[Required]
public virtual App App { get; set; }
}

Asp.net Mvc Code First Many to Many with Additional Properties

As far as i know, i have two way to implement many-to-many relation in asp.net mvc using code-first.
1- Fluent Api
public class HrPerson
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<HrPersonTitle> HrPersonTitle { get; set; }
}
public class HrPersonTitle
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<HrPerson> HrPerson { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<HrPerson>()
.HasMany(s => s.HrPersonTitle)
.WithMany(c => c.HrPerson)
.Map(t =>
{
t.MapLeftKey("HrPersonId")
.MapRightKey("HrPersonTitleId")
.ToTable("HrMapPersonTitle");
});
}
2-Custom Mapping Table
public class HrPerson
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<HrMapPersonTitle> HrMapPersonTitle { get; set; }
}
public class HrPersonTitle
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<HrMapPersonTitle> HrMapPersonTitle { get; set; }
}
public class HrMapPersonTitle
{
public int Id { get; set; }
public int HrPersonId { get; set; }
public int HrPersonTitleId { get; set; }
public virtual HrPerson HrPerson { get; set; }
public virtual HrPersonTitle HrPersonTitle { get; set; }
public string Note { get; set; }
public bool Deleted { get; set; }
}
My questions:
If i choose second way, i am not able to reach HrPersonTitle.Name property from HrPerson model in the view. How can i reach the properties ?
If i choose the first way i can reach the HrPersonTitle.Name but i am not able to add more property in the map file ? How can i add more properties?
Regards.
When you create a M2M without a payload (just the foreign key relationships, no extra data), EF collapses the relationship so that you can query directly without having to explicitly go through the join table. However, if you need a payload, then EF can no longer manage the relationship in this way.
So, if you want to get the title, you have to go through HrMapPersonTitle:
#foreach (var title in Model.HrMapPersonTitle)
{
#title.HrPersonTitle.Name
}
Both these methods seem overkill maybe. I don't know your full intentions however I implement this all the time at work and I use the following:
public class HrPerson
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<HrPersonTitle> HrPersonTitles { get; set; }
}
public class HrPersonTitle
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<HrPerson> HrPersons { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<HrPerson>()
.HasMany(s => s.HrPersonTitles)
.WithMany(c => c.HrPersons);
}
If you are using code first and you try and access either mapping within the DbContext it should Lazy Load your information and every property should be accessible.
I do have one question though. Are you sure it should be many to many, do they really have multiple titles?

Asp.Net MVC Codefirst Model Relation to the Same Model

I have a model and i want to put an extra field which can be populated form the same model. IE: Categories and and sub-categories.
In my example, visitor can add an filetype but if file type is under an another file type, he can choose,
But i cant work it out. Below you can see my model.
public class HrFileType
{
[Key]
public int Id { get; set; }
[Display(Name = "Dosya Adı")]
public int Name { get; set; }
public int? HrFileTypeId { get; set; }
public virtual HrFileType HrFileType2 { get; set; }
}
You just need to add a ForeignKeyAttribute like below:
public class HrFileType
{
[Key]
public int Id { get; set; }
[Display(Name = "Dosya Adı")]
public int Name { get; set; }
public int? HrFileTypeId { get; set; }
[ForeignKey("HrFileTypeId")]
public virtual HrFileType HrFileType2 { get; set; }
}
You can also use fluent API to achieve this:
public class HrFileType
{
[Key]
public int Id { get; set; }
[Display(Name = "Dosya Adı")]
public int Name { get; set; }
public int? HrFileTypeId { get; set; }
public virtual HrFileType HrFileType2 { get; set; }
}
public class YourDbContext : DbContext
{
public DbSet<HrFileType> HrFileTypes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//
modelBuilder.Entity<HrFileType>()
.HasOptional(c => c.HrFileType2)
.WithMany()
.HasForeignKey(c => c.HrFileTypeId);
}
}
Have you tried listing the other file types?
public class HrFileType
{
[Key]
public int Id { get; set; }
[Display(Name = "Dosya Adı")]
public int Name { get; set; }
public List<HrFileType> RelatedTypes { get; set; }
}
then using Entity Frameworks fluent API in the DbContext, try explicitly declaring a many to many map.
modelbuilder.Entity<HrFileType>().HasMany(x => x.RelatedTypes).WithMany();
I'd be very interested to see if this works. It's the only logical solution I can think of without having some kind of parent class.

multiple "1 to 0..1" relationship models

I am using this tutorial from microsoft to create a one-zero-to-one relationship with EF4.1 Between an Instructor and OfficeAssignment. This is working like a charm.
But now I want to add a Home for each Instructor (1 to zero-or-1) like in this:
I added the Home model exactly the same way as the OfficeAssignment (like in the tutorial above), but when I try to add controllers for these model, I get the error "An item with the same name has already been added".
So my model is set up incorrectly.
What is wrong with the below?
How do I create multiple one-to-zero-to-one relationships in EF4.1?
public class Instructor
{
public Int32 InstructorID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public virtual OfficeAssignment OfficeAssignment { get; set; }
public virtual Home Home { get; set; }
}
public class OfficeAssignment
{
[Key]
public int InstructorID { get; set; }
public string Location { get; set; }
public virtual Instructor Instructor { get; set; }
}
public class Home
{
[Key]
public int InstructorID { get; set; }
public string Location { get; set; }
public virtual Instructor Instructor { get; set; }
}
public class Context : DbContext
{
public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
public DbSet<Instructor> Instructors { get; set; }
public DbSet<Home> Homes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Instructor>()
.HasOptional(p => p.OfficeAssignment)
.WithRequired(p => p.Instructor);
modelBuilder.Entity<Instructor>()
.HasOptional(p => p.Home).WithRequired(p => p.Instructor);
}
Doesn't look like EF supports real 1 to 0..1 relationship. You need a foreign key. And add the optional (int?) into the main model.
So I did this as follow, and it works like a charm.
public class Instructor
{
public Int InstructorID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public int? OfficeAssignmentID { get; set; }
public virtual OfficeAssignment OfficeAssignment { get; set; }
public int? HomeID { get; set; }
public virtual Home Home { get; set; }
}
public class OfficeAssignment
{
public int OfficeAssignmentID { get; set; }
public string Location { get; set; }
}
public class Home
{
public int HomeID { get; set; }
public string Location { get; set; }
}

asp.net mvc automapping view model to domain model

Is it possible to automap UserViewModel to User?
public class User
{
[Key]
public virtual int UserId { get; set; }
public virtual string Name { get; set; }
public virtual string Surname { get; set; }
[Required]
public virtual string Username { get; set; }
[Required]
public virtual string Password { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
public class UserViewModel
{
public User User { get; set; }
public List<Role> AvailableRoles { get; set; }
public List<Role> AssignedRoles { get; set; }
public int[] AvailableSelected { get; set; }
public int[] AssignedSelected { get; set; }
public string SavedAssigned { get; set; }
}
AutoMapper.Mapper.CreateMap<UserViewModel, User>();
AutoMapper.Mapper.Map(model, user);
I've tried this, but it won't work. I need it for my Edit action method in the User controller to save the changes to the database.
Have you tried
AutoMapper.Mapper.CreateMap<UserViewModel, User>()
.ForMember(dest => dest, opt => opt.MapFrom(src => src.User));

Resources