Let s say i have two tables, "article" and "event". They both have comments so they both need access to the "comments" entity.
How do i declare this relationship? having two foreign keys in "comments" is problematic since one of the foreign keys will be null in every case.
Can i just not declare any foreign key? Is there a convention on how to deal with this situation?
public class Article
{
public int Id { get; set; }
public virtual IEnumerable<Comment> comments { get; set; }
}
public class Event
{
public int Id { get; set; }
public virtual IEnumerable<Comment> comments { get; set; }
}
public class Comment
{
public int Id { get; set; }
public Article article { get; set; }
public Event event{ get; set; }
}
I would maybe consider a dedicated comment table per entity. One comment table for article and another for event. Especially if individual comments never apply to both articles and events.
However, there is nothing that prevents you from making a shared one with two FKs as you described.
Related
I am creating a web api that needs to return details about vehicles. The first part works fine, just returning data from my vehicles table. Now I have another table which may or may not contain additional data about vehicles in the first table. So when I get vehicle data, I want all of the vehicle data, and any additional data from the second table if it exists, like a left join in SQL.
Here are my classes (very much abridged for readability):
public class Vehicle
{
[Key]
[Required]
public string registrationNo { get; set; }
public string capacity{ get; set; }
public string maxGross{ get; set; }
}
public class VehicleDvlaDetail
{
[ForeignKey("Vehicle")]
public string? registrationNumber { get; set; }
public int? co2Emissions { get; set; }
}
And in my context class OnModelCreating I have (again, very abridged):
modelBuilder.Entity<Vehicle>(entity =>
{
entity.HasOne(dvlaRec => dvlaRec.dvlaDetail).WithMany().HasForeignKey(dvla => dvla.registrationNo);
});
This works fine when there is an associated record in the DVLA table, but that isn't always the case. I need to keep them as separate entities as my API will be required to return details from the DVLA table separately as well. Is there any way to create an optional foreign key, as clearly, what I am doing is wrong.
Friendly advice:
Primary key as a string is not a good practice because of performance issues when data table has lots of data in it.
It would be better if you create your model like this:
public class Vehicle
{
public long Id { get; set; }
public string RegistrationNo { get; set; }
public string Capacity { get; set; }
public string MaxGross { get; set; }
public List<VehicleDvlaDetail> VehicleDvlaDetails { get; set; }
}
public class VehicleDvlaDetail
{
public long? VehicleId { get; set; }
public int? Co2Emissions { get; set; }
public Vehicle Vehicle { get; set; }
}
Vehicle and VehicleDvlaDetail are now connected without additional code in OnModelCreating method and it is possible to fetch vehicles with details like this (this is assuming you have named properties in dbcontext Vehicles and VehicleDvlaDetails):
_dbContext.Vehicles.Include(x => x.VehicleDvlaDetails).ToList();
Also as foreign key VehicleId is nullable - this allows for vehicles not to have any dvla details.
Wow. I spent about 3 hours looking for the answer, just posted the question and came across this:
Create an optional foreign key using the fluid-API for Entity Framework 7
So simple...
I have 2 tables. User and Roles.One user can have meany roles. so i created another table called RoleUser. how should i implement this in code first(i want to insert and update operation)
thanks in advance
Natively you should be able to declare the two tables (with properties pointing to the other) and EF will pick up on the many-to-many relationship (and create the intermediary table with two FK's)
public class user
{
public int id { get; set; }
public string username { get; set; }
// user can be within multiple roles
public ICollection<role> roles { get; set; }
}
public class role
{
public int id { get; set; }
public string name { get; set; }
// role can have many users
public ICollection<user> users { get; set; }
}
I found several questions on here relating to this but I'm not quite there. I'm trying to add a second UserProfile property to the already existing CourseRegistration class. When I try to perform the migration I get "The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "FK_dbo.CourseRegistrations_dbo.UserProfile_InstructorId"
I thought I could solve it with some fluent configuration but it had no effect. Based on what I've read I think the issue is that there is existing data that won't allow this.
Question 1: I don't completely understand what it's complaining about and would like to understand it better if anyone could shed some light.
Question 2: Is there any workaround other than dropping the table or removing the data? I don't mind doing that this time, but I'm sure there are situations where that's not an option.
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
}
public class CourseRegistration
{
[Key]
public int RegistrationId { get; set; }
public int UserId { get; set; }
public int? InstructorId { get; set; }
public virtual UserProfile user { get; set; }
public virtual UserProfile Instructor { get; set; }
}
Thanks,
joel
See this, and try this:
public class CourseRegistration
{
[Key]
public int RegistrationId { get; set; }
public virtual UserProfile user { get; set; }
[ForeignKey("InstructorId")]
public virtual UserProfile Instructor { get; set; }
}
I am unable to get the one to one relationship with EF to work properly. I've scored blogs, SO, and msdn docs, but nothing I do seems to work.
I have two models, a Class and an Exam that look like the following:
[Table("classes")]
public class Class
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(255), Required]
public string Title { get; set; }
public virtual Exam Exam { get; set; }
}
[Table("exams")]
public class Exam
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[DisplayFormat(DataFormatString = "{0:h:mm tt}")]
public DateTime? Time { get; set; }
public int ClassId { get; set; }
public virtual Class Class { get; set; }
}
I want to be able to access the exam from the Class and the Class from the Exam, but no matter what I do, I find some error.
Trying to create/run migrations I get the following.
The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
If I add this to my context:
protected override void OnModelCreating(DbModelBuilder builder)
{
builder.Entity<Exam>()
.HasRequired(e => e.Class)
.WithOptional(c => c.Exam);
}
I get the following error:
System.Data.Entity.Infrastructure.DbUpdateException: Entities in 'BenchContext.Exams' participate in the 'Class_Exam' relationship. 0 related 'Class_Exam_Source' were found. 1 'Class_Exam_Source' is expected. ---> System.Data.UpdateException: Entities in 'BenchContext.Exams' participate in the 'Class_Exam' relationship. 0 related 'Class_Exam_Source' were found. 1 'Class_Exam_Source' is expected.
I'm not sure how to tell the fluent api how to correctly foreign key between the two models and nothing I do seems to affect it.
What am I missing?
You are trying to build one-to-one relation through ClassId property in Exam class. That requires ClassId to be unique (= unique constraint) but unique constraint are not supported by EF yet.
The only way EF currently supports real one-to-one relation is by sharing primary key:
[Table("classes")]
public class Class
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(255), Required]
public string Title { get; set; }
public virtual Exam Exam { get; set; }
}
[Table("exams")]
public class Exam
{
[Key, ForeignKey("Class")]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.None)]
public int Id { get; set; }
[DisplayFormat(DataFormatString = "{0:h:mm tt}")]
public DateTime? Time { get; set; }
public virtual Class Class { get; set; }
}
The Id in Exams table is both PK and FK to Classes table. It cannot have autogenerated value. While this is one-to-one relation it still has 1 - 0..1 multiplicity (Class can exist without Exam but Exam must have Class).
I implemented a bidirectional 1:1 relationship based on this answer:
Primary /Foreign Key in Entity Framework
I define the bidirectional relation this way:
public class Student
{
public virtual int StudentId { get; set; }
public virtual Anamnesis Anamnesis { get; set; }
. . .
}
public class Anamnesis
{
[Key, ForeignKey("Student")]
public int AnamnesisId { get; set; }
public virtual Student Student { get; set; }
. . .
}
where, Student is the principal entity and Anamnesis it the entity that shares the PK.
Now I'd like that the relationship created had a Delete Rule = CASCADE. Actually, the relationship that is being created has Delete Rule = NO ACTION as seen in the following picture:
If I manually delete this relation inside the Table Properties window and add other relation with Delete Rule = CASCADE, the code works as I expect allowing me to delete a Student and it's shared Anamnesis that has the same ID.
So, here goes my question:
Is there a way of using Data Annotation (not Fluent API) in my class so that I get a Relation with CASCADE delete rule? I'd prefer using Data Annotation but if it's not possible, I'd be happy with some Fluent API code that makes this work.
NOTE
I have tried the Fluent API code that is shown in this post. It doesn't work in my case where I have bidirectional properties.
The following fluent API code perfectly switch on the cascade delete on the database:
public class Student
{
public virtual int StudentId { get; set; }
public virtual Anamnesis Anamnesis { get; set; }
}
public class Anamnesis
{
public int AnamnesisId { get; set; }
public virtual Student Student { get; set; }
}
public class Context : DbContext
{
public DbSet<Student> Students { get; set; }
public DbSet<Anamnesis> Anamnesises { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>()
.HasRequired(s => s.Anamnesis)
.WithRequiredPrincipal(a => a.Student)
.WillCascadeOnDelete();
}
}
Also, you can use [Required] Attribute, and it will automatically set the delete rule to "CASCADE" mode in related relationship. (and also set "Allow Null" property of that entity to "false" in DB)