EF CodeFirst One-to-One - asp.net-mvc

I find thousands examples for codefirst relations, but i can't do work my sample !
Many errors like this:
The ForeignKeyAttribute on property 'LanguageID' on type 'BL.Objects.User' is not valid. The navigation property 'Language' was not found on the dependent type 'BL.Objects.User'. The Name value should be a valid navigation property name.
and same same same...
I really want to load language association with user. (en, ru, es)
public abstract class BaseUser : FinanceBase<int>, IUser
{
[ForeignKey("Language")]
public int LanguageID { get; set; }
[ForeignKey("LanguageID")]
public virtual Language Language { get; private set; }
}
public class User : BaseUser
{
public override void GenerateID()
{
...
}
}
public abstract class BaseLanguage : FinanceBase<int>, ILanguage
{
#region Implementation of ILanguage
public string Code { get; set; }
public string Fullname { get; set; }
public string ImagePath { get; set; }
#endregion
}
public class Language : BaseLanguage
{
public override void GenerateID()
{
}
}
public class FinanceDatabaseContext : DbContext
{
public FinanceDatabaseContext()
{
Database.SetInitializer(new FinanceContextInitializer());
}
public DbSet<User> Users { get; set; }
public DbSet<Language> Languages { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Users");
}).HasKey(x => x.ID).HasRequired(x => x.Language).WithMany().HasForeignKey(x => x.LanguageID);
modelBuilder.Entity<Language>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Languages");
}).HasKey(x => x.ID);
base.OnModelCreating(modelBuilder);
}
}
public class FinanceContextInitializer : DropCreateDatabaseIfModelChanges<FinanceDatabaseContext>
{
protected override void Seed(FinanceDatabaseContext context)
{
context.Database.ExecuteSqlCommand("ALTER TABLE Users ADD CONSTRAINT uc_Language UNIQUE(LanguageID)");
}
}
Thanks !

You don't need to use foreign key for LanguageID
public int LanguageID { get; set; }
[ForeignKey("LanguageID ")]
public virtual Language Language { get; private set; }

Add a virtual navigation field to your User table for language if it isnt already there.
Ie you have LanguageId and Language in the POCO class User.
The alternative is a navigation field in Language Class back to User,
ie Public virtual List Users
But lets stick to Users having BOTH virtual navigation property and Foreign Key Id field.
... the rest of User.....
public int LanguageId
// nav relationship
public virtual Language Language { set; get; }
// Now the FK declaration as you described should work in fluent API...
// Has required NAVIGATION property, its 1 to many and the I have a field for this FK value called X
modelBuilder.Entity<User>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Users");
}).HasKey(x => x.ID).HasRequired(x => x.Language).WithMany().HasForeignKey(x => x.LanguageID);

Solved. My Navigation property Language has private setter
[ForeignKey("LanguageID")]
public virtual Language Language { get; private set; }

Related

Implementing Many-To-Many relationship in Asp.Net (confused?)

My current aim is to build a database structure using classes in Entity Framework & ASP MVC.
I currently have a Users table and a Posts table. What I would like to do is create a many to many relationship for Users who have liked Posts (whilst conserving who created the post). And be able to access for each user all of the posts they have liked. Currently I have these classes but I'm unsure of how to link them as all of the online examples are linking Primary Keys from different databases where I just want to use the Username Parameter. Any help would be great. I have this so far.
public class Posts
{
[Key]
public virtual int PostId { get; set; }
public virtual string Title { get; set; }
public virtual string URL { get; set; }
[DisplayName("Main Text")]
public virtual string TextBody { get; set; }
public int PostLikes { get; set; }
private DateTime Datedata = DateTime.Now;
public DateTime PostDate { get { return Datedata; } set { Datedata = value; } }
public virtual Users User { get; set; }
public ICollection<PostLikes> UsersWhoHaveSigned { get; set; }
}
{
public class Users
{
[Key]
public virtual int UserId { get; set; }
public virtual string Username { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual List<Posts> Post { get; set; }
}
}
I have not built the UsersWhoHaveSigned table yet. Early experimentation caused me so much backtracing it was painful. Any help would be great.
Edit: I was hoping to ask for help and then appropriate that informtaion to fit my example which utilises the individual accounts add-on. This produces some addition files that are now causing interference with the code you've provided.
Here is the IdentityModels.cs file.
using System.Data.Entity;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
namespace Coursework2.Models
{
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit https://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
}
I believe that the assembly directives at the top are preventing system.data.entity from being used so when I try to implement ApplicationDbContext : DbContext I get error messages :/
Ideally I'm looking to use the IdentityModels.cs file as a replacement for the users class. But still very lost.
First of all, I recommend that you use the singular form for your class names, as EF will automatically pluralize table names.
Second, for a key property, you can just use the term Id, without any annotations, and EF will pick it up as the principal key.
Finally, I'll assume you are looking to use a Code-First approach. Consider the following classes (yours, but refactored for clarity purpose):
public class Post
{
public virtual Guid Id { get; set; }
public virtual string UserName { get; set; }
public virtual User User { get; set; }
public virtual ICollection<PostLike> Likes { get; set; }
}
public class PostLike
{
public virtual Guid Id { get; set; }
public virtual Guid PostId { get; set; }
public virtual Post Post { get; set; }
public virtual string UserName { get; set; }
public virtual User User { get; set; }
}
public class User
{
public virtual Guid Id { get; set; }
public virtual string UserName { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public virtual ICollection<PostLike> Likes { get; set; }
}
To make it work, you'd need a DbContext such as the following. Pay attention to the OnModelCreating method, which is where the magic happens:
public class ApplicationDbContext
: DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Post> Posts { get; set; }
public DbSet<PostLike> PostLikes { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>()
.HasAlternateKey(u => u.UserName);
modelBuilder.Entity<User>()
.HasMany(u => u.Posts)
.WithOne(p => p.User);
modelBuilder.Entity<Post>()
.HasOne(p => p.User)
.WithMany(u => u.Posts)
.HasForeignKey(p => p.UserName)
.HasPrincipalKey(u => u.UserName);
modelBuilder.Entity<Post>()
.HasMany(p => p.Likes)
.WithOne(pl => pl.Post);
modelBuilder.Entity<PostLike>()
.HasOne(pl => pl.Post)
.WithMany(p => p.Likes);
modelBuilder.Entity<PostLike>()
.HasOne(pl => pl.User)
.WithMany(u => u.Likes)
.HasForeignKey(pl => pl.UserName)
.HasPrincipalKey(u => u.UserName);
}
}
Voila! I hope it answers your question ;)
If so, please don't forget to mark my post as the answer!
Edit:
I'll provide some explanations, that I had left out to answer your question ASAP.
So, first thing you need to do, is to declare the UserName as an alternate key, because you want to create relationships depending on it, and you already have the 'Id' principal key declared.
Second, on each object that should own a User reference base on the UserName alternate key, you need to declare the object's UserName property as the foreign key of the relationship, and the User's UserName property as the principal key.
In other words, the foreign key is the property that a referencing object uses for the relationship, and the principal key is the property based on which the referenced object is bound to the referencing one.
Note that principal keys must have a key or alternate key constraint, or it won't work.
Just to comment on your answer. I found that I had to use
using Microsoft.EntityFrameworkCore and remove System.Data.Entity - This was causing the program to be confused as to which DbContext I wanted to use. Thanks!

Defining multiple Foreign Key for the Same table in Entity Framework Code First

I have two entities in my MVC application and I populated the database with Entity Framework 6 Code First approach. There are two city id in the Student entity; one of them for BirthCity, the other for WorkingCity. When I define the foreign keys as above an extra column is created named City_ID in the Student table after migration. Id there a mistake or how to define these FKs? Thanks in advance.
Student:
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public int BirthCityID { get; set; }
public int LivingCityID { get; set; }
[ForeignKey("BirthCityID")]
public virtual City BirthCity { get; set; }
[ForeignKey("LivingCityID")]
public virtual City LivingCity { get; set; }
}
City:
public class City
{
public int ID { get; set; }
public string CityName { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
To achieve what you want you need to provide some aditional configuration.Code First convention can identify bidirectional relationships, but not when there are
multiple bidirectional relationships between two entities.You can add configuration (using Data Annotations or the Fluent API) to present this
information to the model builder. With Data Annotations, you’ll use an annotation
called InverseProperty. With the Fluent API, you’ll use a combination of the Has/With methods to specify the correct ends of these relationships.
Using Data Annotations could be like this:
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public int BirthCityID { get; set; }
public int LivingCityID { get; set; }
[ForeignKey("BirthCityID")]
[InverseProperty("Students")]
public virtual City BirthCity { get; set; }
[ForeignKey("LivingCityID")]
public virtual City LivingCity { get; set; }
}
This way you specifying explicitly that you want to relate the BirthCity navigation property with Students navigation property in the other end of the relationship.
Using Fluent Api could be like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().HasRequired(m => m.BirthCity)
.WithMany(m => m.Students).HasForeignKey(m=>m.BirthCityId);
modelBuilder.Entity<Student>().HasRequired(m => m.LivingCity)
.WithMany().HasForeignKey(m=>m.LivingCityId);
}
With this last solution you don't need to use any attibute.
Now, the suggestion of #ChristPratt in have a collection of Student in your City class for each relationship is really useful. If you do that, then the configurations using Data Annotations could be this way:
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public int BirthCityID { get; set; }
public int LivingCityID { get; set; }
[ForeignKey("BirthCityID")]
[InverseProperty("BirthCityStudents")]
public virtual City BirthCity { get; set; }
[ForeignKey("LivingCityID")]
[InverseProperty("LivingCityStudents")]
public virtual City LivingCity { get; set; }
}
Or using Fluent Api following the same idea:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().HasRequired(m => m.BirthCity)
.WithMany(m => m.BirthCityStudents).HasForeignKey(m=>m.BirthCityId);
modelBuilder.Entity<Student>().HasRequired(m => m.LivingCity)
.WithMany(m => m.LivingCityStudents).HasForeignKey(m=>m.LivingCityId);
}
Sheesh. It's been a long day. There's actually a very big, glaring problem with your code, actually, that I completely missed when I commented.
The problem is that you're using a single collection of students on City. What's actually happening here is that EF can't decide which foreign key it should actually map that collection to, so it creates another foreign key specifically to track that relationship. Then, in effect you have no navigation properties for the collections of students derived from BirthCity and LivingCity.
For this, you have to drop down to fluent configuration, as there's no way to configure this properly using just data annotations. You'll also need an additional collection of students so you can track both relationships:
public class City
{
...
public virtual ICollection<Student> BirthCityStudents { get; set; }
public virtual ICollection<Student> LivingCityStudents { get; set; }
}
Then, for Student:
public class Student
{
...
public class StudentMapping : EntityTypeConfiguration<Student>
{
public StudentMapping()
{
HasRequired(m => m.BirthCity).WithMany(m => m.BirthCityStudents);
HasRequired(m => m.LivingCity).WithMany(m => m.LivingCityStudents);
}
}
}
And finally in your context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new Student.StudentMapping());
}

Linking three Models with many to many links between all Models

I've been struggling with this for a few days.
I have three models that link together with many to many relationships.
Rules:
A requirement can have many controls and vice versa
A procedure can have many controls and vice versa
I am currently showing all controls linked to requirements in my requirements views without a problem and i've even got the create / update working through the creation of viewmodels that hold the assigned data
I'd like to show the list of all procedures that are linked to the controls which are linked to the requirement I am viewing. I won't want to edit them at that level as that will be done through the control Controller. It's a link through two join tables that i'm unable figure out :(
Models:
public class Requirement
{
[Key]
public int RequirementId { get; set }
public string Name { get; set; }
public string Details { get; set; }
public virtual ICollection<Control> Controls { get; set; }
}
public class Control
{
public int ControlId { get; set; }
public string Name { get; set; }
public virtual ICollection<Requirement> Requirements { get; set; }
public virtual ICollection<Procedure> Procedures { get; set; }
}
public class Procedure
{
[Key]
public int ProcedureId { get; set; }
public string Name { get; set; }
public string Details { get; set; }
public virtual ICollection<Control> Controls { get; set; }
}
Dbcontext:
public class CompliancePortalContext : DbContext
{
public CompliancePortalContext()
: base("CompliancePortalContext")
{ }
public DbSet<Control> Controls { get; set; }
public DbSet<Procedure> Procedures { get; set; }
public DbSet<Requirement> Requirements { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Procedure>().HasMany(c => c.Controls).WithMany(p => p.Procedures).Map(
mc =>
{
mc.MapLeftKey("ProcedureId");
mc.MapRightKey("ControlId");
mc.ToTable("ProcedureControl");
});
modelBuilder.Entity<Requirement>().HasMany(c => c.Controls).WithMany(r => r.Requirements).Map(
mc =>
{
mc.MapLeftKey("RequirementId");
mc.MapRightKey("ControlId");
mc.ToTable("RequirementControl");
});
base.OnModelCreating(modelBuilder);
}
}
This should give you all the procedures linked to all the controls linked to all requirements.
from r in Requirements
from c in r.Controls
from p in c.Procedures
select p

Ef Code first One to one relationship with id as foreign

I'm traying to do a mapping with One to One relationship with id as "foreign", I can't change the database
Those are the tables
Cutomer
int CustomerId
string Name
CustomerDetail
int CustomerId
string Details
Entity Splittitng does not works for me since i need a left outter join.
Any Ideas?
Thanks in advance,
and sorry about my english.
You can use the Shared Primary Key mapping here.
public class Customer
{
public int CustomerId { get; set; }
public string Name { get; set; }
public virtual CustomerDetail CustomerDetail { get; set; }
}
public class CustomerDetail
{
public int CustomerId { get; set; }
public string Details { get; set; }
public virtual Customer Customer { get; set; }
}
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<CustomerDetail>().HasKey(d => d.CustomerId);
modelBuilder.Entity<Customer>().HasOptional(c => c.CustomerDetail)
.WithRequired(d => d.Customer);
}
}

Inheritance and Many-to-Many Relationship

I have the following business role that I need to model:
A bidder could rate a seller as long as they've interacted with this person
A bidder could rate an item only if he had won the auction
The final rating for the seller though, is the average taken from the item rating and the others' ratings on himself.
The rating itself (whether for the item or the user) is the average of scores on several questions.
Accordingly, I thought I should create a Ratings class, then inherit it with UserRating and ItemRating. Both of those should have an ICollection of RatingQuestion (which will eventually be a static table). The questions for the UserRating are different from those of the ItemRating, but I thought it's not really worth creating separate tables/entities for the questions (or maybe I should do a TPH inheritance?).
So, here's what I got so far:
public abstract class Rating
{
public int Id { get; set; }
public virtual User By { get; set; }
}
public class UserRating : Rating
{
public virtual User For { get; set; }
public virtual ICollection<RatingQuestion> Questions { get; set; }
}
public class ItemRating : Rating
{
public virtual Item For { get; set; }
public virtual ICollection<RatingQuestion> Questions { get; set; }
}
public class RatingQuestion
{
public int Id { get; set; }
public string Text { get; set; }
public virtual ICollection<Rating> Rating { get; set; }
}
The reason why I am putting the ICollection inside the sub-classes rather than the Rating base class is because the RatingQuestion for both is different, but I'm not sure that's the way I should be doing it, correct me if I'm wrong please.
One thing I need some help with is deciding whether to go for a TPH or a TPT inheritance. I want to keep things simple, but I would also want to keep my database normalized. Moreover, performance is a factor that needs to be taken into account.
Now the last thing I need to know how to do is: how to map the many-to-many relationship between the rating classes (the base class or sub-classes, not sure about which one I should be using) and the RatingQuestion class using the Fluent API AND add an attribute (score) which is a property of the relationship itself so I could record the score on every separate RatingQuestion.
I hope that was clear enough. All suggestions are most welcome. Thanks in advance!
UPDATE: (after Ladislav Mrnka's answer)
public abstract class Rating
{
public int Id { get; set; }
public virtual User By { get; set; }
public virtual ICollection<RatingQuestion> RatingQuestions { get; set; }
}
public class UserRating : Rating
{
public virtual User For { get; set; }
public virtual ICollection<Question> Questions { get; set; }
}
public class ItemRating : Rating
{
public virtual Item For { get; set; }
public virtual ICollection<Question> Questions { get; set; }
}
public class User
{
public int Id { get; set; }
//more properties
public virtual ICollection<UserRating> OwnRatings { get; set; }
public virtual ICollection<UserRating> RatingsForOthers { get; set; }
public virtual ICollection<ItemRating> ItemRatings { get; set; }
}
public class Item
{
public int Id { get; set; }
//more properties
public virtual ItemRating Rating { get; set; } //because an Item will have only one rating
}
public class UserRatingConfiguration : EntityTypeConfiguration<UserRating>
{
public UserRatingConfiguration()
{
HasOptional(p => p.By)
.WithMany(u => u.RatingsForOthers)
.IsIndependent()
.Map(m => m.MapKey(c => c.Id, "RatingSubmitter"));
HasRequired(p => p.For)
.WithMany(u => u.OwnRatings)
.IsIndependent()
.Map(m=>m.MapKey(c => c.Id, "RatedSeller"));
}
}
public class ItemRatingConfiguration : EntityTypeConfiguration<ItemRating>
{
public ItemRatingConfiguration()
{
HasRequired(p => p.By)
.WithMany(u => u.ItemRatings)
.IsIndependent()
.Map(m=>m.MapKey(c => c.Id, "ItemRatingSubmitter"));
}
}
I'm getting a very messed up model in SQL Server, which is obviously caused by my messed up mapping. Any suggestions or should I just forget about inheritance and the DRY principle all together in the case at hand?
You can't use direct M:N mapping if you need to add custom property to that relation. In such case you need to model junction table as another entity which will hold reference to Rating and Question and also include Score property.
I would recommend using TPH inheritance. It is easier to use and it has better performance. TPT constructs really ugly queries. Also there is no reason to have RatingQuestions in derived classes. Both these collections reference same type so you can move it to parent. Moreover according to this question there are some problems with navigation properties in child classes when using TPH. I'm not sure if this problem is still valid in Code-first. Anyway your current model simply don't need navigation property on child.
If you follow my advices you don't need to add any mapping. It will map with default conventions when using these classes:
public abstract class Rating
{
public virtual int Id { get; set; }
public virtual User By { get; set; }
private ICollection<RatingQuestion> _ratingQuestions = null;
public ICollection<RatingQuestion> RatingQuestions
{
get
{
if (_ratingQuestions == null)
{
_ratingQuestions = new HashSet<RatingQuestion>();
}
return _ratingQuestions;
}
protected set { _ratingQuestions = value; }
}
}
public class ItemRating : Rating
{
public virtual Item For { get; set; }
}
public class UserRating : Rating
{
public virtual User For { get; set; }
}
public class RatingQuestion
{
public virtual int Id { get; set; }
public virtual int Score { get; set; }
public virtual Rating Rating { get; set; }
public virtual Question Question { get; set; }
}
public class Question
{
public virtual int Id { get; set; }
public virtual string Text { get; set; }
private ICollection<RatingQuestion> _ratingQuestions = null;
public ICollection<RatingQuestion> RatingQuestions
{
get
{
if (_ratingQuestions == null)
{
_ratingQuestions = new HashSet<RatingQuestion>();
}
return _ratingQuestions;
}
protected set { _ratingQuestions = value; }
}
}

Resources