Link Tables with Properties in EF6 Code First - c#

I currently have a situation with code first EF6 where I need to create a many-to-many mapping (easy enough) however the generated relationship table needs to contain properties of its own. Here's a simplified example of what I have:
public class Journey : Entity
{
public string Name { get; set; }
public DateTime Start { get; set; }
public virtual ICollection<Point> Points { get; set; }
}
public class Point : Entity
{
public DbGeography GeoLocation { get; set; }
public string Name { get; set; }
public virtual ICollection<Journey> Journeys { get; set; }
}
The "Entity" class basically contains the primary key.
This would of course create a relationship table with foreign keys for the "Point" and "Journey" table primary keys. Great, but what if I want to add properties to that link table? Such as a DateTime property called ArrivalDate which holds the time of arrival at that point.
You could argue that I can add that property to the "Point" class but I want this table to hold a list of all physical points used in my application, without duplicates (a single point could be used in multiple journeys, each with different arrival dates). Therefor I need to hold this property elsewhere, the link table would be ideal here.
The only solution I can think of would be to create an actual relationship class:
public class JourneyPoint : Entity
{
public int JourneyId { get; set; }
public int PointId { get; set; }
public DateTime ArrivalTime { get; set; }
public virtual Journey Journey { get; set; }
public virtual Point Point { get; set; }
}
And then modify my Journey and Point classes to have a one-to-many relationship with the JourneyPoint class. However this just adds complication and isn't particularly semantic:
...
Journey myJourney = aMethodToGetMyJourney();
Point firstPoint = myJourney.JourneyPoints[0].Point.
...
That doesn't seems to make much sense and could confuse other developers. Is what I'm asking for possible at all?

Related

Would the Entity Framework's navigational properties work if I drop foreign key constraints from the database?

As you know that developers mostly mock the relationship between tables instead of using physical relationships between table (yeah, the line drawn from one table to another if you put a foreign key constraint on the column).
But I believe that Entity Framework doesn't work properly if physical relationships aren't there for navigational properties.
So, is there any way around?
My classes:
public class Phones
{
public int ID { get; set; }
public string Model { get; set; }
public string Manufacturer { get; set; }
public List<Users> Users { get; set; }
}
public class Sims
{
public int ID { get; set; }
public string Code { get; set; }
}
This creates a 1-M relationship from User -> Sims.
But what if I drop the foreign key constraint and leave it as it is, how will the navigational properties work then?
At this case better to remove references from both classes and handle relations manually outside of these classes:
public class Sims
{
public int ID { get; set; }
public string Code { get; set; }
//public User User { get; set; }
public int UserID { get; set; }
}

EF Core - How to create inherited entity types that contain foreign key columns

I am using the table per hierarchy approach for achieving inheritance in entity types. I have 3 classes defined:
Room - Base class
SubMapRoom - Inherits from Room
OverviewRoom - Inherits from Room
In the DB, I just have 1 table called Room that has both the SubMapRoom and OverviewRoom columns in it. It also contains the Discriminator column for specifying which type it is.
First, I attempted to move all of the SubMapRoom columns in the Room class into the SubMapRoom class. 1 of the columns contains a foreign key to a different table called Status. After doing this, I tried specifying the foreign key relationship for the SubMapRoom entity type in OnModelCreating(). However, I get a compile error when I try to do this. In the EF Core OnModelCreating() method, I have this code (marked the line that contains the error below):
modelBuilder.Entity<SubMapRoom>(entity =>
{
entity.HasOne(d => d.UnassignedDoctorStatus)
.WithMany(p => p.Room) **ERROR HAPPENS HERE**
.HasForeignKey(d => d.UnassignedDoctorStatusId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("FK_Room_UnassignedStatusID");
});
modelBuilder.Entity<Room>()
.HasDiscriminator<int>("RoomType")
.HasValue<SubMapRoom>(1)
.HasValue<OverviewRoom>(2);
I get this error:
Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type
I know that I can solve this by changing the other class (Status) to use the inherited type instead of the base type for the navigation property, but that seems like the wrong way to go. I feel like I am missing something here. What would be the correct way to define a foreign key relationship in an inherited entity type?
[EDIT]
Here are the classes for the 4 models I have referenced here:
public abstract class Room
{
public Room()
{
InverseLinkedRoom = new HashSet<Room>();
}
public int Id { get; set; }
public int SubMapId { get; set; }
public string MapLabel { get; set; }
public string RoomLabel { get; set; }
public int LeftCoordinate { get; set; }
public int TopCoordinate { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int? LinkedRoomId { get; set; }
public int RoomType { get; set; }
public Room LinkedRoom { get; set; }
public SubMap SubMap { get; set; }
public PatientQueue PatientQueue { get; set; }
public ICollection<Room> InverseLinkedRoom { get; set; }
}
public class SubMapRoom : Room
{
public int? UnassignedDoctorStatusId { get; set; }
public Status UnassignedDoctorStatus { get; set; }
}
// Note: Have not yet attempted to move base class members in here
public class OverviewRoom : Room
{
}
public partial class Status
{
public Status()
{
Room = new HashSet<Room>();
}
public int Id { get; set; }
public string EnumId { get; set; }
public bool Active { get; set; }
public bool IsFastBlink { get; set; }
public ICollection<Room> Room { get; set; }
}
Thanks for the help everyone. I reviewed my DB schema and decided to make some changes that make this problem go away. It actually turns out that my new schema is easier to use in the code than I originally thought. In fact, it's a lot easier. I think I was trying to overengineer this. So sometimes, the solution is to review your schema and figure out if it even makes sense in the first place. Basically, what I did was move the inherited classes to a separate table with a separate ID. Because, at the end of the day, they are logically separate types of entities and only related in terms of the data. In the code, they serve much different purposes even though they share some of the same columns.
At the end of it all, the only disadvantage of this approach is that I am violating DRY on another table (there are 5 repeated columns in it). Otherwise, a lot of other operations are easier to code than before. I am willing to live with that instead of dealing with all of this for now. Later, I can try to use Table Per Hierarchy if I am having to add tons of new columns to both tables.

Configure One-None/One Relationship with Multiple Tables using Entity

I'm in a situation where one table has two One-None/One Relationships. How do I implement this using Entity Framework Code-First?
I've seen the following links
https://www.safaribooksonline.com/library/view/programming-entity-framework/9781449317867/ch04s07.html
https://cpratt.co/0-1-to-1-relationships-in-entity-framework/
https://www.tektutorialshub.com/one-to-one-relationship-entity-framework/
Where essentially it's said that the dependent end needs to have a primary key that is the same as that of the principal end. But I'm weary of implementing this with more than one One-None/One Relationship without confirmation and proper knowledge of what's going on. Furthermore I am not sure how to construct statements as it does not have a conventional Foreign Key.
I've also seen Configuring multiple 1 to 0..1 relationships between tables entity framework which confused me beyond recognition.
See below for the relevant part of my DB Diagram:
So Essentially, a Player shouldn't be saved without a DKImage, similarly a Product shouldn't be saved without a DKImage.
Below is the code for Models: Players, Products, DKImages (I know it's not correct, I only implemented it this way so I can generate the database and show the diagram)
Player
public enum Positions { PG, SG, SF, PF, C }
public class Player
{
[Key]
[ForeignKey("Images")]
public int PlayerID { get; set; }
[Required]
public string PlayerName { get; set; }
[Required]
public string PlayerLastName { get; set; }
[Required]
public int PlayerAge { get; set; }
[Required]
public Positions Position { get; set; }
[Required]
public bool Starter { get; set; }
[Required]
[Display(Name = "Active / Not Active")]
public bool Status { get; set; }
//Foreign Keys
public int PlayerStatsID { get; set; }
//Navigation Properties
[ForeignKey("PlayerStatsID")]
public virtual IQueryable<PlayerStats> PlayerStats { get; set; }
public virtual DKImages Images { get; set; }
}
DKImages
public class DKImages
{
[Key]
public int ImageID { get; set; }
[Required]
public string ImageURL { get; set; }
[Required]
public DateTime DateUploaded { get; set; }
//Foreign Keys
[Required]
public int CategoryID { get; set; }
//Navigation Properties
public virtual Products Products { get; set; }
public virtual Category Category { get; set; }
public virtual Player Player { get; set; }
}
Products
public class Products
{
[ForeignKey("Images")]
[Key]
public int ProductID { get; set; }
[Required]
public string ProductName { get; set; }
[Required]
public DateTime DateAdded { get; set; }
//Foreign Keys
[Required]
public int ProductTypeID { get; set; }
//Navigation Properties
[ForeignKey("ProductTypeID")]
public virtual ProductType ProductType { get; set; }
public virtual DKImages Images { get; set; }
}
Edit
I have been told that the code above is correct. If so then how do I create CRUD LINQ Statements (Or any method of constructing CRUD statements for that matter) with the above code.
What you want here is referred to as polymorphic associations: several entities having child entities of one type. They're typically used for comments, remarks, files etc. and usually applied to 1:n associations. In your case there are polymorphic 1:1 associations. Basically these associations look like this (using a bit more generic names):
How to implement them?
Entity Framework 6
In EF6 that's problem. EF6 implements 1:1 associations as shared primary keys: the child's primary key is also a foreign key to its parent's primary key. That would mean that there should be two FKs on Image.ID , one pointing to Person.ID and another one pointing to Product.ID. Technically that's not a problem, semantically it is. Two parent entities now own the same image or, stated differently, an image should always belong to two different parents. In real life, that's nonsense.
The solution could be to reverse the references:
But now there's another problem. The entity that's referred to is named the principal, the other entity is dependent. In the second diagram, Image is the principal, so in order to create a Person, its image must be inserted first and then the person copies its primary key. That's counter-intuitive and most likely also impractical. It's impossible if images are optional.
Nevertheless, since in your case you want images to be required let me show how this association is mapped in EF6.
Let's take this simple model:
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public virtual Image Image { get; set; }
}
public class Product
{
public int ID { get; set; }
public string Name { get; set; }
public virtual Image Image { get; set; }
}
public class Image
{
public int ImgID { get; set; } // Named for distinction
public string Url { get; set; }
}
The required mapping is:
modelBuilder.Entity<Image>().HasKey(pd => pd.ImgID);
modelBuilder.Entity<Person>().HasRequired(p => p.Image).WithRequiredDependent();
modelBuilder.Entity<Product>().HasRequired(p => p.Image).WithRequiredDependent();
As you see, Image has two required dependents. Perhaps that's better than two required parents, but it's still weird. Fortunately, in reality it's not a problem, because EF doesn't validate these associations. You can even insert an image without a "required" dependent. I don't know why EF doesn't validate this, but here it comes in handy. The part WithRequiredDependent might as well have been WithOptional, it doesn't make a difference for the generated data model, but at least this mapping conveys your intentions.
An alternative approach could be inheritance. If Person and Product inherit from one base class this base class could be the principal in a 1:1 association with Image. However, I think this is abusing a design pattern. People and products have nothing in common. From a design perspective there's no reason for them to be part of one inheritance tree.
Therefore, in EF6 I think the most feasible solution is to use the third alternative: separate image tables per entity.
Entity Framework Core
In EF-core 1:1 associations can be implemented the EF6 way, but it's also possible to use a separate foreign key field in the dependent entity. Doing so, the polymorphic case looks like this:
The Image class is different:
public class Image
{
public Image()
{ }
public int ImgID { get; set; }
public int? PersonID { get; set; }
public int? ProductID { get; set; }
public string Url { get; set; }
}
And the mapping:
modelBuilder.Entity<Person>().Property(p => p.ID).UseSqlServerIdentityColumn();
modelBuilder.Entity<Person>()
.HasOne(p => p.Image)
.WithOne()
.HasForeignKey<Image>(p => p.PersonID);
modelBuilder.Entity<Product>().Property(p => p.ID).UseSqlServerIdentityColumn();
modelBuilder.Entity<Product>()
.HasOne(p => p.Image)
.WithOne()
.HasForeignKey<Image>(p => p.ProductID);
modelBuilder.Entity<Image>().HasKey(p => p.ImgID);
Watch the nullable foreign keys. They're necessary because an image belongs to either a Person or a Product. That's one drawback of this design. Another is that you need a new foreign key field for each new entity you want to own images. Normally you want to avoid such sparse columns. There's also an advantage as compared to the EF6 implementation: this model allows bidirectional navigation. Image may be extended with Person and Product navigation properties.
EF does a pretty good job translating this into a database design. Each foreign key has a filtered unique index, for example for Person:
CREATE UNIQUE NONCLUSTERED INDEX [IX_Image_PersonID] ON [dbo].[Image]
(
[PersonID] ASC
)
WHERE ([PersonID] IS NOT NULL)
This turns the association into a genuine 1:1 association on the database side. Without the unique index it would be a 1:n association from the database's perspective.
An exemple in your Player table would be this :
public class Player
{
// All the rest you already coded
[Required]
public int ImageID
[ForeignKey("ImageID")]
public virtual DKImage DKImage {get;set;}
}
This would force a player to have a DKImage, but as said in the comments, this create a one to many relationship.
Another way out would be to put all Player fields into the DKImage table, those fields would be null if there is no player associated to this DKImage.
Edit for 1 to 1..0
Ivan Stoev's link got some pretty interesting insight on how to accomplish this :
https://weblogs.asp.net/manavi/associations-in-ef-4-1-code-first-part-3-shared-primary-key-associations
It seems like you will have to put a bit more code in your class :
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<DKImage>().HasOptional(t => t.Player).WithRequired();
}
If the tutorial is correct, this would read as :
"DKImage entity has an optional association with one Player object but this association is required for Player entity".
I have not tested it yet.

Multiple Many-to-Many relationsship Entity Framework

The main goal is the ability to have a many to many relationship between the table Mucle and Exercise. I want an Exercise to have both a primary and a secodary muscle group.
Is it possible to have two icollections in one model and only one in the other?
If someone could help with the "fluent configuration" as well, I would appreciate it!
Here is the code I have got right now.
public class Muscle
{
public int MuscleID { get; set; }
public bool IsFront { get; set; }
public string Name { get; set; }
public virtual ICollection<Exercise> Exercises { get; set; }
}
public class Exercise
{
public int ExerciseID { get; set; }
// ExerciseCategory
public int ExerciseCategoryID { get; set; }
public DateTime CreationDate { get; set; }
public string Description { get; set; }
public string Name { get; set; }
public virtual ExerciseCategory ExerciseCategory { get; set; }
public virtual ICollection<Muscle> Muscles { get; set; }
public virtual ICollection<Muscle> MusclesSecondary { get; set; }
}
No way to map the model you described.
To map your model (2 n-m relationship) you would need a Junction table with a discriminator and you can't do it with EF.
You have several way to change your model to make it work with EF
You create a model (a class) for the Junction table and insert a discriminator in it. Your model changes (and I think that the new model is less clear)
Why is there a Muscles and MusclesSecondary? Can it be discriminated with an attribute of Muscle? In this case you can have the attribute in Muscle and remove Exercise.MusclesSecondary Then you have only an n-m relationship that EF handles with a Junction table.
If you want this model you can add 2 collections to Muscle (for example ExcercisesMuscle and ExercisesMusclesSecondary) and a 3rd not mapped collection where you have the content of ExcercisesMuscle and ExercisesMusclesSecondary toghether. About the ExcercisesMuscle and ExercisesMusclesSecondary they can be observable collections so you can cache the content of Exercises collection in an efficient way.

EF Code First - How do you specify foreign key name for child object table used by different type of parents

I've got some objects that look like this:
abstract public class Field
{
public int Id { get; set; }
public string Name { get; set; }
public int Ordinal { get; set; }
}
[Table("DropDownField")]
public class DropDownField : Field
{
public virtual List<FieldOption> Options { get; set; }
}
[Table("RadioButtonField")]
public class RadioButtonField : Field
{
public virtual List<FieldOption> Options { get; set; }
}
public class FieldOption
{
public int Id { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
In my database, it ends up creating the a FieldOptions table using Code First. However, it creates the following columns:
Id
Name
Value
DropDownField_Id
RadioButtonField_Id
What I'd like to see is just one Field_Id in this table since the Id of a field has to be unique across the different types of fields.
Is there a way to do this? I've done some searching but I must not know the right search terms to use to find the answer.
imho, what you want, from a relational database point of view, is a column (Option.FieldId) being a foreign key to 2 tables DropDownField and RadioButtonField.
That is whenever you insert an option, FieldId must reference an existing DropDownField AND an existing RadioButtonField.
That is at least weird.
I don't think this can/should be achieved.

Categories