understanding entity framework core foreign key relationships - c#

Im utilsing a code first approach for the first time (Ive previously always used database first) and am trying to understand some basic concepts. If I create a foreign key relationship between two entities, how does entity framework know which properties (columns) to use in the two sides of the relationship ? My question is probably better explained with a simple code example. I have two entities, patient and treatment. A patient can have multiple treatments so there will be a one to many relationship between the patient and the treatment, with a foreign key relationship existing between the two entities. Here are my entity classes. Please note these are greatly simplified for the sake of explanation.
public class Patient
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<PatientTreatment> PatientTreatment { get; set; }
}
public class PatientTreatment
{
public int Id { get; set; }
public string TreatmentDescription { get; set; }
public int PatientId { get; set; }
public virtual Patient Patient { get; set; }
}
So for the patient entity the primary key would be Id and for the PatientTreatment entity, its primary key would also be Id
For the foreign key relationship, according to what Ive googled so far, the code above will create that relationship for me, is this correct ? If so, how would entity framework know that the PatientId in PatientTreatment is linked to Id in the Patient entity ? This is how its supposed to be in the database (SQL Server), but I cant see how entity framework would know this. Im really new to the code first approach so Im just trying to understand how this would work. Could anyone explain this to me ?
Ive also read that setting the relationship as above doesnt create indexes (PatientId in PatientTreatment) so these have to be created in code as well

EF works with conventions, as Caius mentioned.
In your case:
EF knows that there are two entity object - Patient and PatientTreatment, because dbSet and optional configuration exist for those classes.
Patient contains so called navigation property leading to PatientTreatment's - a collection, but it could be most of the things implementing IEnumerable - EF assumes that You want to create relationship here.
Patient have an Id field - EF by naming convention without any configuration will assume that this is an entity key. Same goes for PatientTreatment
PatientTreatment has a navigation property to a single Patient - this, again, by convention tells EF that you want the relationship between this two entities to be one-to-many - collection on one side, single reference on the other side.
Ofc one to many could also be possible by convention even without navigation property in PatientTreatment - just to be clear.

Related

Entity framework: Foreign entity without requiring foreign entity to actually exist entity to exist

I have an audit-tracking like system, that contains the following two entities:
The JobCreate entity:
public class JobCreate
{
[Key] public string JobId { get; set; }
public List<AffectedEntity> AffectedEntities { get; set; }
}
And the AffectedEntity entity:
public abstract class AffectedEntity
{
[Required]
public string JobId { get; set; }
public int Id { get; set; }
[CanBeNull] public JobCreate Job { get; set; }
}
So far this is just a normal foreign key relation:
modelBuilder.Entity<JobCreate>()
.HasMany(j => j.AffectedEntities)
.WithOne(a => a.Job)
.HasForeignKey(a => a.JobId)
.IsRequired(false)
.OnDelete(DeleteBehavior.Cascade);
Entity Framework generates a foreign key for this relationship. My problem with this is that this audit system is event driven, which means it receives the events that creates the AffectedEntity and the event that creates the JobCreate entries out of order. In other words, the JobCreate entity might not yet exist when the AffectedEntity is created. However as far as the domain goes, this is actually fine. So how do I model that in Entity Framework? I want to be able to "navigate" along that connection from JobCreate to AffectedEntity, however the other direction is not necessary.
the JobCreate entity might not yet exist when the AffectedEntity is created. However as far as the domain goes, this is actually fine. So how do I model that in Entity Framework?
Just have the relationship in the EF model, but omit it or set the FK to not be enforced in the back-end. EG in SQL Server you would set the Foreign Key Constraint to NOCHECK.
Just beware that EF may assume that the FK is enforced when it creates queries. EG if you query db.AffectedEntities.Inclue("JobCreate") it may use an INNER JOIN and not return any AffectedEntities without a JobCreate.
And if you need to deal with AffectedEntities with a null JobID, you'd have to change the data type to int?.

Entity Framework - Multiple 1 to 0..1 relationships using the same Key

I've read as many posts as I can on this topic but none of the solutions I have tried seem to work. I have an existing database and created a new Code First From Existing Database project.
I have a base table called Thing. Every object has a record in this table using Id as the Unique Primary Key. Each other object inherits from this but they use the same Id in the child tables without using a new Identity column in the sub tables. Effectively giving each 'Thing' a unique Id:
public class Thing
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Car
{
public int Id { get; set; }
//other properties
}
public class Person
{
public int Id { get; set; }
//other properties
}
public class Color
{
public int Id { get; set; }
//other properties
}
Every new record first creates an item in 'Thing' and then using that Id value creates a new record in its respective table, creating multiple 1 to 0..1 relationships where the Id field on the derived tables is also the FK to Thing.
Thing 1 to 0..1 Car
Thing 1 to 0..1 Person
Thing 1 to 0..1 Color
and so on
I have tried many different Data Annotation and Fluent API combinations but it always comes back to the same error:
'Unable to retrieve metadata for Model.Car'. Unable to determine the principal end of association between the types 'Model.Thing' and 'Model.Car'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.'
I did manage to get past this error by using virtual with the inverse annotation and setting the Id field to be Key and ForeignKey, but then the message jumps to Person. If you then set it up the same as Car the message reverts back to Car.
It seems I could go back and create a normal Foreign Key to each child table, but that is a lot of work and I am sure it is possible to get this working somehow. Preferably using fluent API.
If you are going to use Data Annotations, you need to declare the PK of the dependent entity as FK too:
public class Thing
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Car Car{get;set;}
}
public class Car
{
[Key,ForeignKey("Thing")]
public int ThingId { get; set; }
//other properties
public virtual Thing Thing{get;set;}
}
And if you are going to use Fluent Api (remove the attributes from your model), the configuration would be like this:
modelBuilder.Entity<Car>().HasRequired(c=>c.Thing).WithOptional(t=>t.Thing);
Based on the multiplicity that is specified, it only makes sense for Thing to be the principal and Car to be the dependent, since a Thing can exist without a Car but a Car must have a Thing.
As you can see you don't need to specify that ThingId is the FK of this relationship.This is because of Entity Framework’s requirement that the primary key of the dependent be used as the foreign key. Since there is no choice, Code First will just infer this for you.
Update
Reading again your question I think you are trying to create a hierarchy. In that case you could use the Table per Type (TPT) approach.

Define Many Tables to One Table relationship in Code First approach

I am in the process of building up a data model in Entity Framework using the Code First approach, but one part has me a bit stumped. The title on this question may be a bit confusing, so I will explain my problem in detail. The length of this post may be daunting, but I think it's a fairly straightforward problem.
I have one model defined like this:
public class KeyValuePair
{
[Key]
[MaxLength(128)]
[Column(Order = 0)]
public virtual string OwnerId { get; set; }
[Key]
[MaxLength(128)]
[Column(Order = 1)]
public virtual string Key { get; set; }
public virtual string Value { get; set; }
}
My intent is for this to just define a generic table for storing key-value properties on other entities in the system. I am using GUIDs for all of my Ids, so OwnerId should uniquely refer to one entity in the system, and the pair (OwnerId, Key) should uniquely identify one property on one entity.
In other words, I want to allow multiple tables in my system to have a One->Many relationship to this KeyValuePair table.
So for example, if I wanted to store the height of a Person who has the ID b4fc3e9a-2081-4989-b016-08ddd9f73db0, I would store a row in this table as:
OwnerId = "b4fc3e9a-2081-4989-b016-08ddd9f73db0"
Key = "Height"
Value = "70 in."
So now I want to define navigation properties from the parent entities to this table, like (to take the Person example):
public class Person
{
[Key]
public virtual string Id { get; set; }
public virtual string Name { get; set; }
// I want this to be a navigation property
public ICollection<KeyValuePair> Properties { get; set; }
}
But I'm not sure how do define the relationship between Person and KeyValuePair so that Entity Framework knows that it should look up the Person's properties by matching the Person's Id against the KeyValuePairs' OwnerId. I can't define a foreign key in the KeyValuePair model, because the OwnerId is going to refer to Ids in several different tables.
It looks like I can do the following to define a relationship from Person to KeyValuePair in OnModelCreating:
modelBuilder.Entity<Person>()
.HasMany(p => p.Properties).WithMany().Map(mp =>
{
mp.MapLeftKey("Id");
mp.MapRightKey("OwnerId", "Key");
mp.ToTable("PersonDetail");
});
Or I could even give the KeyValuePairs their own unique IDs, get rid of OwnerId, and do this:
modelBuilder.Entity<Person>()
.HasMany(p => p.Properties).WithMany().Map(mp =>
{
mp.MapLeftKey("Id");
mp.MapRightKey("Id");
mp.ToTable("PersonDetail");
});
But both of these approaches involve the creation of an intermediary table to link the Person and KeyValuePair tables, and that seems like excessive overhead in terms of bloating my database schema and requiring more expensive JOINs to query the data.
So is there a way to define the relationship such that I don't need to involve intermediary tables? Am I going about this database design the wrong way?
Side note: For anyone wondering why I am using this approach to define properties on my entities rather than simply adding fixed properties to the data model, I am using fixed properties in the data model where applicable, but the application I am building requires the ability to define custom properties at runtime. I also think this question is applicable to other potential scenarios where multiple tables have a One->Many relationship to a shared table.
The only way I can think of doing it (and I'll admit, this is not the best of ideas, but it will do what you're asking) would be to have any classes that need to have this relationship with KeyValuePair implement an abstract class that contains the fully implemented navigational property, as well as the ID field. By "fully implemented" I don't mean an actual, mapped relationship; I mean that it should use a DbContext to go out to the KeyValuePair table and actually grab the relevant properties given the ID.
Something like this:
public abstract class HasKeyValuePairs
{
[Key]
public virtual string Id { get; set; }
[NotMapped]
public ICollection<KeyValuePair> Properties
{
get
{
using(var db = new DbContext())
{
return db.KeyValuePairs.Where(kvp => kvp.OwnerID == this.ID);
}
}
}
}
Assuming you're using Lazy Loading (given that you're using the virtual keyword), there shouldn't be much extra overhead to doing it like this, since EF would have to go back to the database anyway to pick up the properties if you ever called for them. You might need to have that return a List just to avoid any potential ContextDisposedException later on in your code, but that at least will get you up and running.

Should I have the foreign key column in my entity models?

I am coming from the Entity Framework over to NHibernate. When looking at how to create my domain entities I noticed that in some of the examples they don't include the column of the foreign key relationship. Since the Session class contains a Load() method it is possible to just use objects without the trip to the database instead of primary keys. Is this a normal practice when constructing entity models in NHibernate.
Example Entity
public class BlogPost : Entity
{
public virtual string Name { get; set; }
//Should this be here
public virtual int AuthorID { get; set; }
public virtual Author Author { get; set; }
}
Creating Entity
BlogPost post = new BlogPost
{
Name = "My first post",
Author = session.Load<Author>(1) //Avoids hitting the database
};
session.Save(post);
-- OR ---
BlogPost post = new BlogPost
{
Name = "My first post",
AuthorID = 1 //Use the id of the object
};
session.Save(post);
You should use full entities / objects instead of having foreign keys.
Foreign keys are database concept. They don't make much sense when you are doing object oriented programming. When doing OOP, you are composing objects together. In your case, a Blog has a collection of Posts. Post has a parent Blog, etc.
Entity IDs are just used to uniquely identify entities.
The whole point of Object-Relational Mapping should be to allow you to use OOP best practices (Object), database best practices (Relational) and not to mix the concepts between them (that's what Mapping part of the name stands for).
Some ORMs are better than others in following this. Hint: NHibernate.

EF4.1 (code first) - How to specify a composite relationship

In Linq to SQL I could specify a relationship that didn't have to depend on the foreign keys and pks existing in the database, useful for creating composite relationships like this:
public class Equipment_CableNormalised
{
...
[Association(ThisKey = "EquipmentId,PortNumber", OtherKey = "EquipmentId,PortNumber", IsForeignKey = false)]
public List<EquipmentPort> EquipmentPorts
{
get; set;
}
}
This then generated the sql similar to " .. join EquipmentPorts EP on EP.EquipmentId = blah and EP.PortNumber = Blah".
Can I do the same sort of thing in EF4.1 (using annotations or fluent api)? I know you can specify composite keys and use the [Keys] and [ForeignKeys] attributes, but this relationship doesn't map to keys...
How does the sample relation from your code works? I expect that EquipementId must be either PK or unique key (not supported in both L2S and EF) on one side because otherwise the relation could not exist (both one-to-one and one-to-many demands unique principal). Once it is PK on one side the port number is redundant.
Code first allows only mapping to keys. If you have existing database you can cheat it in your model and map new relations in the same way as you would map existing but you still have to follow simple rule - properties in principal are primary keys, properties in dependent entity are mapped as foreign keys.
If you want EF to generate DB for you, you will always have all relations in the database.
Use HasKey http://www.ienablemuch.com/2011/06/mapping-class-to-database-view-with.html
Either use HasKey, put this on OnModelCreating
 modelBuilder.Entity<SalesOnEachCountry>().HasKey(x => new { x.CountryId, x.OrYear });   
Or use Key Column Order
public class SalesOnEachCountry
{       
    [Key, Column(Order=0)] public int CountryId { get; set; }
    public string CountryName { get; set; }
    [Key, Column(Order=1)] public int OrYear { get; set; }
     
    public long SalesCount { get; set; }     
    public decimal TotalSales { get; set; }
}
Regarding your question about foreign key, I haven't yet tried the pure code(OnModelCreating) approach, perhaps you can just put two ForeignKey attribute on child class itself, might need to put Column Order too.
This could be the answer composite key as foreign key
That answer confirms my hunch that you could put two ForeignKey attributes on child class itself.

Categories