Foreign Key Relationship EF Code First - c#

I'm busy creating my first EF code first model and I've come across a slightly confusing problem.
I have a number of model classes that each inherit from a base model class that has three common properties I want used in all model classes. These properties are Id, LastUpdated and LastUpdatedBy.
Id is the primary key of each model class.
LastUpdated is a foreign key to my 'User' model class.
LastUpdatedBy is a datetime field that indicates the last time the record was modified.
So what I'd like to setup is the 1 to 1 foreign key relationship from my base class to my 'User' model class but I'm receiving the exception:
Multiplicity is not valid in Role 'Profile_LastUpdatedByUser_Source'
in relationship 'Profile_LastUpdatedByUser'. Because the Dependent
Role properties are not the key properties, the upper bound of the
multiplicity of the Dependent Role must be '*'
This is my ModelBase class:
public class ModelBase
{
public int Id { get; set; }
public DateTime LastUpdated { get; set; }
[ForeignKey("LastUpdatedByUser")]
[Required]
public int LastUpdatedByUserId { get; set; }
public virtual User LastUpdatedByUser { get; set; }
}
This is one of my model classes:
public class Profile : ModelBase
{
[StringLength(25, MinimumLength=1)]
[Required(ErrorMessage="First Name is Required")]
public string FirstName { get; set; }
[StringLength(25, MinimumLength = 1)]
[Required(ErrorMessage = "Last Name is Required")]
public string LastName { get; set; }
[StringLength(25, MinimumLength = 1)]
[Required(ErrorMessage = "Email Address is Required")]
public string Email { get; set; }
public string Mobile { get; set; }
public string HomePhone { get; set; }
public string WorkPhone { get; set; }
public string ImageSource { get; set; }
public Squable.Model.Enums.MembershipType.MembershipTypeEnum MembershipType { get; set; }
}
This is my user class (Please ignore the Password property, I'll fix that later ;) ):
public class User : ModelBase
{
public string UserName { get; set; }
public string Password { get; set; }
public int ProfileId { get; set; }
public virtual Profile Profile { get; set; }
}
I don't know if what I am doing is best practise but I could do with some advice as to how to either fix the problem or maybe just some pointers in the right direction.

Move
public int Id { get; set; }
to User class and to Profile you can also change the names to UserId and to ProfileId and move
public virtual User LastUpdatedByUser { get; set; }
to Profile class.
I have a bad experience with sharing Id in base entity If you are planning to use Repository and UnitOfWork pattern you will get a lot of problems later. Check your current database structure and tables with SQL Server Management Studio.
More Info TPH

Related

Different user types with ASP.NET Core 1.1 Identity and Entity Framework Core

Using ASP.NET Core Identity with Entity framework Core i need to add different types of users in my app:
Let's say that i need two types of users: "Student" and "Teacher"; both of them are also ApplicationUsers since they have to be authenticated to access the app.
I have accomplish that creating two tables: one for Student and one for Teacher. Both tables are one-to-one related with the ApplicationUser table.
I'd like to know if this is correct or if i'm doing something wrong, because when updating the database with migrations it throws the error "FOREIGN KEY 'FK_Student_AspNetUsers_Id' in 'Student' table may cause cycles or multiple cascade paths. specify ON DELETE NO ACTION or UPDATE NO ACTION". And, in any case, if it goes right, at the end i'd have an ApplicationUser class with two columns (one for StudentId and another for TeacherId), and one of them will always be null since an ApplicationUser can be a Student or a Teacher, but not both.
Here is my code so far:
public class ApplicationUser : IdentityUser<int>
{
public string Name { get; set; }
[ForeignKey("Teacher")]
public int? TeacherId { get; set; }
public virtual Teacher Teacher { get; set; }
[ForeignKey("Student")]
public int? StudentId { get; set; }
public virtual Student Student { get; set; }
}
public class Student
{
[Key, ForeignKey("User")]
public int Id { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
public string MotherMaidenName { get; set; }
public DateTime BirthDate { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ApplicationUser User { get; set; }
}
public class Teacher
{
[Key,ForeignKey("User")]
public int Id { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
public string MotherMaidenNAme { get; set; }
public virtual ApplicationUser User { get; set; }
}
UPDATED: I've set TeacherId and StudentId as nullable, so the Error mentioned above is gone.
You shouldn't create different tables for each user type you have.
You should create roles and assign that roles to a user. For example create a role student and a role teacher and assign them acording to your needs.
So I would say what you've done isn't a good design.
When you need to save additional values for a student/teacher than I would do something similar to your design.
But I wouldn't add ID's for my student/teacher to my ApplicationUser class.
I would simply add a UserId to my student/teacher class.
So the design issue should be that you're trying to put that stuff into the ApplicationUser class.

One to one relationship with nullable foreign key in Entity Framework

I'm trying to create database relationship between two tables.
I have redemption codes which can be free. But one user has only one redem code.
I created two entities:
public class UserProfile
{
[Key]
[ForeignKey("User")]
public int Id { get; set; }
//... other fields
public virtual RedemptionCode RedemptionCode { get; set; }
public virtual User User { get; set; }
}
public class RedemptionCode
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("UserProfile")]
public int? UserProfileId { get; set; }
public virtual UserProfile UserProfile { get; set; }
[Required]
public string Code { get; set; }
}
But when I adding a migration I have the following error:
One or more validation errors were detected during model generation:
RedemptionCode_UserProfile_Source: : Multiplicity is not valid in Role 'RedemptionCode_UserProfile_Source' in relationship 'RedemptionCode_UserProfile'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
What do I really want? I want to store free redem codes in db and link one of them with new user in my system.
Here is the right code for the UserProfile class
public class RedemptionCode
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key,ForeignKey("UserProfile")]
public int Id { get; set; }
public int? UserProfileId { get; set; }
[ForeignKey("UserProfile")]
public virtual UserProfile UserProfile { get; set; }
[Required]
public string Code { get; set; }
}
You have to move the ForeignKey from the property UserProfileId to the navigation property UserProfile and indicate that UserProfile is the principal in the relationship by adding the ForeignKey attribute to the Id of the RedemptionCode class

Map same Model class for multiple purposes in Entity FrameWork

I have two model classes one is ApplicationUser and the second is Appointment. Application user includes all users that use the application, in my case, Doctors and Data entry operators. Doctors will be assigned to each appointment and Data entry operators will be making this log to DB. I want to map both these users with appointment. I have tried something like this
public class Appointment
{
public int AppointmentID { get; set; }
public DateTime Date { get; set; }
public int DoctorID { get; set; }
[ForeignKey("DoctorID")]
public virtual ApplicationUser Doctor { get; set; }
public int SystemUserID { get; set; }
public virtual ApplicationUser SystemUser { get; set; }
}
public class ApplicationUser : IdentityUser
{
public string Email { get; set; }
public string Mobile { get; set; }
public string FirstNsme { get; set; }
public string LastName { get; set; }
}
But this throws an error
Appointment_Doctor_Target_Appointment_Doctor_Source: : The types of all properties in the Dependent Role of a referential constraint must be the same as the corresponding property types in the Principal Role. The type of property 'DoctorID' on entity 'Appointment' does not match the type of property 'Id' on entity 'ApplicationUser' in the referential constraint 'Appointment_Doctor'.
Can anyone point out why this error is occurring and what is the correct approach to this problem?
IdentityUser as all entities in asp.net identity entity framework have string as key. You are trying to map to an int. So either use Guids as foreign keys in your Appointment entity
public class Appointment
{
[Key]
public int AppointmentID { get; set; }
public DateTime Date { get; set; }
public string DoctorID { get; set; }
[ForeignKey("DoctorID")]
public virtual ApplicationUser Doctor { get; set; }
public string SystemUserID { get; set; }
[ForeignKey("SystemUserID ")]
public virtual ApplicationUser SystemUser { get; set; }
}
or change the type of Ids in identity classes to int. You can find help here.
There are multiple issue in your classes.
What is DoctorID? Where it is defined?
You need to first focus on establishing correct relationship between your entities logically.
I think your Appointment class need not contain SystemUserID who added an appointment.
Second if you wanted to share some properties between two user types than create a common class and derive in Doctor and SystemUser.
Add DoctorId into Doctor table along with specific details pertaining to Doctor e.g. Specialty.
SystemUser adds a appointment so the table should contain data related to that i.e. doctorId and appointmentId.
Update:
Based on your comment, you could do something like this. Note its for reference only, you are better person to define a better DB Schema.
public class Appointment
{
public int AppointmentID { get; set; }
public DateTime Date { get; set; }
public int DoctorID { get; set; }
[ForeignKey("ApplicationUserId")]
public virtual ApplicationUser Doctor { get; set; }
public int SystemUserID { get; set; }
[ForeignKey("ApplicationUserId")]
public virtual ApplicationUser SystemUser { get; set; }
}
public class ApplicationUser
{
public int ApplicationUserId { get; set; }
public string Email { get; set; }
public string Mobile { get; set; }
public string FirstNsme { get; set; }
public string LastName { get; set; }
public UserType UserType { get; set; }
}
public enum UserType
{
Doctor,
SystemUser
}
FURTHER AND MORE COMPLEX ERROR:
I had this error multiple times across 4 linked tables.
Each table had composite keys of 3 - 7 fields,
and one table referenced its own 3-field key with a different mix of its own columns.
I struggled for ages with fixing one sequence of fields (which does fix the error as mentioned in other posts) only to have knock-on effect in other entities.
The solution:
Align all linked tables' FK fields in order of reducing occurrence
ORIGINALLY:
AFTER KEY FIELDS WERE ALIGNED:
And re-ordered all anonymous FK objects in FluentAPI in the DbContext to match the new order.
This fixed all headaches.

Entity Framework Two foreign keys as primary key

So i am working with entity code firest and i have a user class that looks like this:
public class User
{
[Key]
public string Username { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime LastModified { get; set; }
}
I am trying to make a "friends table" and no matter what i come up with I get an error on the db creation. This is what I Currently have in the Friends Class:
public class Friend
{
[Key, ForeignKey("User")]
public virtual User MyUser { get; set; }
[Key,ForeignKey("User")]
public virtual User MyFriend { get; set; }
public bool IsAccepted { get; set; }
}
this is the error i get:
The ForeignKeyAttribute on property 'MyUser' on type 'Core.Model.Friend' is not valid. The foreign key name 'User' was not found on the dependent type 'Core.Model.Friend'. The Name value should be a comma separated list of foreign key property names.
What am I missing?
You need to use the Column attribute. Normally I would use something like this:
public class Friend
{
[Key]
[Column(Order = 0)]
public int MyUserId { get; set; }
[Key]
[Column(Order = 1)]
public int MyFriendId { get; set; }
[ForeignKey("MyUserId")]
public virtual User MyUser { get; set; }
[ForeignKey("FriendId")]
public virtual User MyFriend { get; set; }
public bool IsAccepted { get; set; }
}
I'm not sure what would happen if you map the Column attribute directly to the navigation property. You can try it if you like and see what happens.. but the above generally works for me.
Alternatively, if you change to use fluent mapping, you can do something like this:
HasKey(u => new { u.MyUserId , u.MyFriendId });

One entity to another with multiple properties

I have a problem with the table creation of EF5 Code first. I'm going to demonstrate two cases and their outputs. What I want to know what the difference between them and how I could be successful on my goal.
Case 1
public class User{
public int ID { get; set; }
public string Name { get; set; }
public ICollection<UserMessage> Messages { get; set; }
}
public class UserMessage{
public int ID { get; set; }
public string Message { get; set; }
public User CreatedBy { get; set; }
public ICollection<User> PermittedUsers {get; set;}
}
Now in this case, entity framework generates only User and UserMessage classes that are only binding to each other with UserMessage_ID in User and CreatedBy_ID in UserMessage.
The problem that I'm curious is in here. How will EF5 recognize permitted users whenever I create a new message? Is it going to duplicate the same message for each permitted users? Or what?
Case 2
public class User{
public int ID { get; set; }
public string Name { get; set; }
public ICollection<UserMessage> Messages { get; set; }
}
public class UserMessage{
public int ID { get; set; }
public string Message { get; set; }
public ICollection<User> PermittedUsers {get; set;}
}
In this case I have removed just only CreatedBy in UserMessage entity. Now EF5 generates 3 tables named : User, UserMessage, UserMessageUsers. The links between UserMessage and User is now over UserMessageUsers so that we can easily manage permitted users.
How can I add CreatedBy property into UserMessage without breaking up Case 2 ?
You can use fluent api to configurate it:
modelBuilder.Entity<UserMessage>()
.HasMany(d=>d.PermittedUsers)
.WithMany(d=>d.Messages)
.Map(c=>c.ToTable("UserMessageUsers"));

Categories

Resources