I would like to know what rules Entity Framework follows in regards to the naming/generation of navigation properties. I have observed several scenarios which don't seem to make sense so I was wondering if anyone knows exactly how these work.
Scenario 1:
public class Post
{
public int Id { get; set; }
public User Author { get; set; }
}
Generates
ie. by default navigation properties generate FKs named [PropertyName]_Id
Scenario 2:
It makes sense that if EF generates properties such of the format [PropertyName]_Id when you manually specify a FK Id it will follow the same rules however:
public class Post
{
public int Id { get; set; }
public int? Author_Id { get; set; }
public User Author { get; set; }
}
Generates
As you can see this doesn't automatically register as a nav property.
Scenario 3:
If it doesn't work for Scenario 2 why does it work for an alternate naming convention?
public class Post
{
public int Id { get; set; }
public int? AuthorId { get; set; }
public User Author { get; set; }
}
Generates
What are the rules around navigation property detection and generation?
That is expected behavior and it is based on two different conventions based by EF
In the first example you are using Independent association where your entity doesn't have FK property. EF will create FK in the database using simple pattern: NameOfNavigationProperty_NameOfRelatedPK This convention follows traditional database naming.
In the second example you defined property with the same name as FK used by EF. EF detected this and added 1 to its generated FK. The reason why your property is not used as FK is the second convention which searches for FK properties. This convention expects that FK property will have this name (conventions follows traditional .NET naming):
NameOfNavigationPropertyNameOfRelatedPK provided by NavigationPropertyNameForeignKeyDiscoveryConvention
NameOfRelatedTypeNameOfItsPK provided by TypeNameForeignKeyDiscoveryConvention
NameOfRelatedPK provided by PrimaryKeyNameForeignKeyDiscoveryConvention
In the last example you correctly defined FK property and EF detected it so it uses Foreign key association.
In addition to #Ladislav Mrnka's answer above, you can find a detailed reference of the entity framework default conventions here:
http://msdn.microsoft.com/en-us/library/system.data.entity.modelconfiguration.conventions(v=vs.103).aspx
Related
I want to use EF code first to apply the existing database tables modeling.
Let's say I have three tables.
Project table. The primary key is ProjectId, it has other columns such as ProjectName, StartDate and EndDate etc.
Technology table. The primary key is TechnologyId, the other columns are technologyName, Note etc.
ProjectTechnologyLink. Link the two tables together. It has the Primary key ProjectId and TechnologyId also they are the foreign keys.
The sample data in ProjectTechnologyLink
ProjectId TechnologyId CreatedBy CreatedDate
25 3 One 2016-01-01
100 4 One 2016-01-01
100 8 Two 2016-01-01
Assume One project can have many technologies and one technology can exist in many projects.
The model classes are:
For Project:
public class Project
{
public int ProjectId {get;set;}
public string ProjectName {get;set;}
...
public ICollection<ProjectTechnologyLink> ProjectTechnologyLink
}
For Technology:
public class Technology
{
public Technology()
{
ProjectTechnologyLink = new HashSet<ProjectTechnologyLink>();
}
public int TechnologyId {get;set;}
public string TechnologyName {get;set;}
...
public ICollection<ProjectTechnologyLink> ProjectTechnologyLink {get;set;}
}
For ProjectTechnologyLink class.
public class ProjectTechnologyLink
{
public int ProjectId {get;set;}
public int TechnologyId {get;set;}
...
public Project Project {get;set;}
public Technology Technology {get;set;}
}
Then in OnModelCreating method.
modelBuilder.Entity<ProjectTechLink>(entity =>
{
entity.HasKey(e=> new {e.ProjectId, e.TechnologyId});
entity.ToTable("ProjectTechnology", "myscheme");
entity.HasOne(x=>x.Project)
.WithMany(p=>p.ProjectTechnologyLink)
.HasForeignKey(d=>d.ProjectId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_PROJECT_TECHNOLOGY_LINK_PID");
entity.HasOne(x=>x.Technology)
.WithMany(p=>p.ProjectTechnologyLink)
.HasForeignKey(d=>d.TechnologyId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_PROJECT_TECHNOLOGY_LINK_TID");
});
My question is
Is it right for the all code? Sometimes I saw people put some attributes before the properties in the class. But I don't have it.
Your code is very acceptable, and should serve you well, but lets review why there is a mix of Attributes and Fluent API out there.
The EF pipeline has three main points where we can inject database metadata (table configuration), these include:
Attribute Notation
Code First Conventions
Fluent Configuration API
The order above list also depicts the order that they are processed within the pipeline, internally the Attributes are meaningless to the DbContext until they are parsed by built in (or customised) Conventions, these conventions internally use the Fluent API to configure the DbContext.
Different scenarios call for and allow different mixes, generally in a Code First scenario Attribute Notation is preferred over heavy use of the Fluent API. However foreign key constraints that include cascading deletes or many to many relationships such as this are often expressed in Fluent API directly as the syntax can be a bit simpler as well as to ensure that no other conventions can overrule our expected implementation in the database.
Attribute notation allows your table schema to be mostly contained within the class definition, but still allows the application runtime to override the interpretations of these attributes by customising, or disabling built in conventions.
If your metadata can be expressed using attributes, then the entire specification of your schema becomes more concise, which is especially useful when presenting your code solutions online in examples where the structure and relationships are important to be shown. If your online example only needs to express the relationship, then those examples will often only use fluent notation.
- It makes for a heard to implement code example if you need express both the schema and relationship mapping separately.
If you use attribute notation, then your example can be expressed like this, and you will not need anything extra in OnModelCreating:
public class Project
{
[Key]
public int ProjectId { get; set; }
public string ProjectName { get; set; }
...
public virtual ICollection<ProjectTechnologyLink> ProjectTechnologyLink { get; set; } = new HashSet<ProjectTechnologyLink>();
}
NOTE: In the above Project class definition I have used an inline initializer for the ProjectTechnologyLink relationship, i find this style fits in well with Attribute Notation as the default value is now also defined in close proximity with the Property, when initializers are only only defined in the constructor it is easy to forget to include an init at all or it can be hard to find the init logic in code. Now a quick "Got To Definition" will reveal the default implementation as well as any database schema related attributes without having to lookup other resources.
public class Technology
{
public Technology()
{
ProjectTechnologyLink = new HashSet<ProjectTechnologyLink>();
}
[Key]
public int TechnologyId { get; set; }
public string TechnologyName { get; set; }
...
public virtual ICollection<ProjectTechnologyLink> ProjectTechnologyLink { get; set; }
}
public class ProjectTechnologyLink
{
[Key, Column(Order = 0)]
public int ProjectId { get; set; }
[Key, Column(Order = 0)]
public int TechnologyId { get; set; }
...
[ForeignKey(nameof(ProjectId))]
public virtual Project Project { get; set; }
[ForeignKey(nameof(TechnologyId))]
public virtual Technology Technology { get; set; }
}
virtual Navigation Properties:
In EF it is important to mark your navigation properties as virtual members. This will allow EF to implement lazy loading and perform other optimised implementation of those properties when it generates a wrapper class that inherits from your entity class. Even if you do not intend to support Lazy Loading, there are other scenarios where EF will wrap your class, and either way your data definition classes should not be concerned or aware of operational decision that can be made and changed at runtime depending on your context needs.
Conventions:
The previous example demonstrates pure attribute notation. It is very possible to replace the default Conventions with your own for defining primary and foreign keys. Meaning it is theoretically possible to not have any attributes or Fluent notation at all. I try to discourage a pure convention based approach because it makes it a bit harder to find the configuration in a large or distributed schema definition, which is also the same argument I use to discourage a pure Fluent API approach, attributes are the logical place to document the expected usage of a table or field.
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.
I have an entity which is already being used with an underlying database, and it was created with just the navigational property to an optional entity (1:0..1). So by default conventions, EF created a nullable foreign key column in the DB and gave it the "MyProp_Id" name with underscore, according to that convention.
Now, I wish to expose that foreign key as a property on the entity, because it will make certain scenarios easier for me. I don't want to rename/change the underlying foreign key column in the DB (the MyProp_Id one). In fact, there shouldn't be any underlying DB updates, I just want to expose that FK on the entity. A code sample to clarify:
public class MyEntityA
{
public long Id { get; set; }
//public long? MyOptionalEntityB_Id { get; set; } <== this is what I am trying to add
//public long? MyOptionalEntityBId { get; set; } <== this didn't work either
public MyEntityB MyOptionalEntityB { get; set; }
}
I've tried just simply adding the "MyOptionalEntity_Id" property as property on the entity, hoping that EF would "automagically" see that because the names are the same, it would just map and be happy. NO DICE.
Then I tried to name my property "MyOptionalEntityId" (no underscore), but still NO DICE.
Then I tried adding an explicit mapping configuration to say:
this.Property(p => p.MyOptionalEntityId).HasColumnName("MyOptionalEntity_Id");
NO DICE
Is there a way to do this? Is this clear and make sense?
Try adding foreign key attribute.
public long? MyOptionalEntityB_Id { get; set; }
[ForeignKey("MyOptionalEntityB_Id")]
public MyEntityB MyOptionalEntityB { get; set; }
Or using fluent api.
modelBuilder.Entity<MyEntityA >()
.HasOptional(x => x.MyOptionalEntityB)
.WithMany().HasForeignKey(x => x.MyOptionalEntityB_Id);
// ^^^ -> if MyEntityB has collection of MyEntityA, mention it
I thought it might be helpful to get a definitive answer on when to use WithOptionalDependent and when to use WithOptionalPrincipal. The help for the two functions is a little unclear, and I find myself digging through multiple Stack Overflow answers and answers on other sites combining answers in order to feel confident I've got the relationship going the correct direction.
Here's what MSDN says about WithOptionalDependent:
Configures the relationship to be optional:optional without a
navigation property on the other side of the relationship. The entity
type being configured will be the dependent and contain a foreign key
to the principal. The entity type that the relationship targets will
be the principal in the relationship.
and here is what it says about WithOptionalPrincipal:
Configures the relationship to be optional:optional without a
navigation property on the other side of the relationship. The entity
type being configured will be the principal in the relationship. The
entity type that the relationship targets will be the dependent and
contain a foreign key to the principal.
The line "The entity type being configured" is the part that always confuses me (and I assume others).
In this example:
class MyEntityA
{
[Key]
public int Id { get; set; }
public int BId { get; set; }
[ForeignKey("BId")]
public MyEntityB B { get; set; }
}
class MyEntityB
{
[Key]
public int Id { get; set; }
}
modelBuilder.Entity<MyEntityA>().HasOptional(a => a.B).WithOptionalDependent();
is "The entity type being configured" referring to MyEntityA or MyEntityB? I assume it is the former.
If that's correct, what's an example of when you'd use WithOptionalPrincipal?
I actually think in my code example it should really be WithMany and neither of the WithOptional options. Clearly I'm still confused!
There are overloads for both of these functions that take the navigation property going the other direction. I assume those overloads don't change those answers, but please correct me if I'm wrong.
I hope this will be helpful to the larger community as well.
For example, lets modify your EntityB by navigation property and make BId nullable (as we are talking about optional relationship).
class MyEntityA
{
[Key]
public int Id { get; set; }
public int? BId { get; set; }
[ForeignKey("BId")]
public virtual MyEntityB B { get; set; }
}
class MyEntityB
{
[Key]
public int Id { get; set; }
public virtual MyEntityA A { get; set; }
}
then we can use:
modelBuilder.Entity<MyEntityB>().HasOptional(a => a.A).WithOptionalPrincipal();
MyEntityA has FK to MyEntityB, so in your example you configure MyEntityA and use WithOptionalDependent. But you can start configuration from MyEntityB-side, then you need WithOptionalPrincipal.
The answer to your question is: "The entity type being configured" is MyEntityA
This can be seen definitively by looking at the documentation for
OptionalNavigationPropertyConfiguration<TEntityType, TTargetEntityType>
which is the type returned by HasOptional and which says:
TTargetEntityType
The entity type that the relationship targets.
which provides more context for the phrases:
The entity type being configured
The entity type that the relationship targets
So, in your case you get back from HasOptional an
OptionalNavigationPropertyConfiguration<MyEntityA, MyEntityB>
Thus, WithOptionalDependent means that MyEntityB will be the Principal with an optional Navigation Property pointing back to MyEntityA (specified via the overload's lambda parameter) and MyEntityA will be the Dependent and contain a Foreign Key and Navigation Property (as specified in the lambda parameter of HasOptional). This is the scenario in your model.
Conversely, WithOptionalPrincipal means that MyEntityA will be the Principal and MyEntityB the Dependent with Foreign Key and Navigation Property.
I'm using ASP.NET MVC 3 with the Entity Framework 4 code first approach and every time I try to specify composite keys using the key attribute on my models, I get this error:
System.Data.Edm.EdmAssociationConstraint: : Number of Properties in
the Dependent and Principal Role in a relationship constraint must be
exactly identical.
I'm using the column attribute to differentiate ordering of the primary keys like so:
public class Game
{
[Key, Column(Order=0)]
public Guid GameId { get; set; }
[Key, Column(Order=1)]
public string Name { get; set; }
public string Description { get; set; }
public Game()
{
this.GameId = Guid.NewGuid();
}
}
I would like to know if there is another approach to creating composite keys, or perhaps there is a way to stop getting this error? I know that it's possible to add logic to the OnModelBuild event, but I'd rather use the key attributes on the model if possible.
Try to exclude property Name from the entity key (that I would recommend). Or, use it in all entities if you really need to make it part of the key.