EF Code First: Primary Key same as Foreign Key - entity-framework

I have two classes
public class Product
{
public Guid Id { get; set; }
public string ProductDetails { get; set; }
}
public class SpecialProductDetails
{
public Guid Product_Id { get; set; } // PK and FK to Product class
public string SpecialName { get; set; }
public string SpecialDescription { get; set; }
public virtual Product Product { get; set; }
}
SpecialProductDetails is mapped 1-1 with Product class and is optional. It shares the same PrimaryKey and ForeignKey.
In Fluent API i am mapping this relationship like this (inside SpecialProductDetails)
public SpecialProductDetails()
{
HasKey(p => p.Product_Id);
HasRequired(p => p.Product).WithMany().HasForeignKey(p => p.Product_Id).WillCascadeDelete(true);
}
This gives me this error when trying to generate the Database
\tSystem.Data.Entity.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role 'SpecialProductDetails_Product_Source' in relationship 'SpecialProductDetails_Product_Source'. Because the Dependent Role refers to the key properties, the upper bound of the multiplicity of the Dependent Role must be '1'.
How can i have a column set as PK and FK on EF Code First?

I'm quite sure you have already solved that, but I hit the same problem and the solution I found was:
public SpecialProductDetails()
{
HasKey(p => p.Product_Id);
HasRequired(p => p.Product).WithOptional();
}
"it worth noting that when we are mapping a one-to-one association with fluent API, we don't need to specify the foreign key as we would do when mapping a one-to-many association with HasForeignKey method. Since EF only supports one-to-one associations on primary keys, it will automatically create the relationship in the database on the primary keys."
after http://weblogs.asp.net/manavi/associations-in-ef-4-1-code-first-part-3-shared-primary-key-associations

Related

How to change foreign key suffix in Entity Framework Core?

In EF Core with a code-first approach, by default column referencing another entity has an Id suffix - for example PersonId.
Is it possible - and if so, how? - to change it to _id, so to person_id?
Create the foreign key explicitly under the name you want - in your case Parent_Id. Keep a navigation property and foreign key property.
public int Parent_ID { get; set; }
public virtual Parent Parent { get; set; }
Map the foreign key relations using .HasForeignKey(). Something similar as below
builder.HasOne(d => d.Prop)
.WithMany(p => p.NavigationProp)
.HasForeignKey(d => d.ForeignKeyProp)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_ConstraintName");
If you prefer data annotation, you could also use
[Column("Parent_ID")]
public int ParentID { get; set; }
To add to WisdomSeeker's answer, you can use a [ForeignKey] annotation to point at a shadow property for the FK.
Given a class like a Course with a Person reference for a Teacher:
public class Course
{
[Key]
public int Id {get; set;}
// other fields.
[ForeignKey("person_id")]
public virtual Person Teacher { get; set; }
}
Alternatives as above would be:
[ForeignKey("Teacher")]
public int person_id { get; set; } // Not recommended naming convention in code.
public virtual Person Teacher { get; set; }
or
[Column("person_id"), ForeignKey("Teacher")]
public int TeacherId { get; set; }
public virtual Person Teacher { get; set; }
I generally avoid adding FK fields into classes as this leads to two sources of truth for what Teacher is assigned to a course. You have course.TeacherId and course.Teacher.Id, which could differ on update prior and after a SaveChanges. Shadow properties help avoid confusion and keep data updates consistent.
Using [Column] is common in Db-First implementations where you want to use a C# naming convention for properties to use in-code, but abide by existing/desired DB naming conventions in the database. I don't generally recommend using DB naming conventions in C# classes.

Could a foreign key property be a part of a composite primary key?

Following the TPC pattern.
I've one abstract class called Entity which is inherited by Person and LegalEntity.
public abstract class Entity
{
public int Id { get; set; }
}
public class LegalEntity : Entity
{
public string CorporateName { get; set;}
}
public class Person : Entity
{
public string Name { get; set; }
}
I've also another one called Agent which have many children. For simplification i'll show only one class Subscriber.
public abstract class Agent
{
public int EntityId { get; set; }
public int RoleId { get; set; }
}
public class Subscriber : Agent
{
public string Number { get; set; }
public Entity Entity { get; set; } // No EntityId here because the foreign key correspond to the Id of Subscriber
}
Below the configuration of my entities :
public AgentConfiguration()
{
HasKey(s => new { s.EntityId, s.RoleId });
...
}
public SubsbcriberConfiguration()
{
Map(m =>
{
m.MapInheritedProperties();
m.ToTable("T_ACTEUR_SOUSCRIPTEUR");
}
**.WithMany() //I don't want a property on the other side
.HasForeignKey(s => s.EntityId); // EF doesn't accept to put the property Id as a foreign key.**
}
public EntityConfiguration()
{
HasKey(e => e.Id);
Property(e => e.Id).HasColumnName("id_personne");
...
}
public PersonConfiguration()
{
Map(m =>
{
m.MapInheritedProperties();
m.ToTable("T_PERSONNE_PHYSIQUE");
});
}
I have a One to One relationship between the abstract class Entity and the concrete class Subscriber.
The primary key of the class Entity is at the same time a part of the composite primary key of the Subscriber and a foreign key.
Is it possible to configure this scenario?
When i declare the property Id as a foreign key, EF throws an exception like below :
The foreign key component 'EntityId' is not a declared property on type 'Subscriber'. Verify that it has not been explicitly excluded from the model and that it is a valid primitive property.
I tried many scenarios, but i didn't find a solution because i have a one to one relationship but the tables have different keys and the foreign key is a part of the composite primary key.
How can i configure a property as a foreign key and also a part of a composite primary key in code first?
What am I missing? Any help will be greatly appreciated!
Edit :
I broke up the inheritance of the class Agent and the TPC. Now the class Subscriber has no parent. when i configure it like below :
public SubsbcriberConfiguration()
{
HasKey(s => new { s.EntityId, s.RoleId });
Property(s => s.EntityId).HasColumnName("id_personne")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
Property(s => s.RoleId).HasColumnName("id_role")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
...
ToTable("T_ACTEUR_SOUSCRIPTEUR");
HasRequired(s => s.Entity)
.WithMany()
.HasForeignKey(s => s.EntityId)
.WillCascadeOnDelete(false);
}
Entity Framework is looking for another foreign key : column Extent1.Entity_Id does not exist
And the query generated by EF when i try to load a subscriber is :
`SELECT
"Extent1"."id_personne",
"Extent1"."id_role",
...
"Extent1"."Entity_Id"
FROM "T_ACTEUR_SOUSCRIPTEUR" AS "Extent1"
WHERE "Extent1"."id_personne" = ((73660)) AND
"Extent1"."id_role" = ((4))`
I don't know how to explain to EF that the foreign key property EntityId is the foreign key.
Could someone shed some light on this?
Thanks.
Well, I'm not sure if I understand your scenario, but this might be helpful.
Entities:
public class Entity
{
public int EntityId { get; set; }
}
public abstract class Agent
{
public int EntityId { get; set; }
public int RoleId { get; set; }
}
public class Subscriber : Agent
{
public string Number { get; set; }
public Entity Entity { get; set; }
}
Mapping:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Entity>()
.HasKey(e => e.EntityId);
//map the Subscriber, not the Agent
modelBuilder.Entity<Subscriber>()
.HasKey(a => new { a.EntityId, a.RoleId });
modelBuilder.Entity<Subscriber>()
.HasRequired(i => i.Entity)
.WithMany()
.HasForeignKey(i => i.EntityId)
.WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
Be aware that it is an "One-to-Many" relationship, not One-to-One. The article you posted on your comment uses UNIQUE CONSTRAINTS to "simulate" the 1:1 relationship in a 1:n relationship. I don't think it is a good approach.
However, let's suppose that you want to follow that way. Where would you put the UNIQUE CONSTRAINT? The FK is part of the composite PK. If you put an UNIQUE CONSTRAINT there, it would break the PK.
Considering that the PK is composed by two columns, and the FK is composed by 1 column of the PK, I don't see how a real 1:1 relationship can be possible. Because to have a 1:1 relationship, the FK column must be the same of PK column.
Hope it helps!

Entity Framework - Fluent API 1 to 0.1 Moderator / Person

I am receiving this error in my migrations:
Person_EventModerator_Target: : Multiplicity is not valid in Role 'Person_EventModerator_Target' in relationship 'Person_EventModerator'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
Here are my models (note: base entity holds the primary key for all models):
public class EventModerator : BaseEntity
{
......
// foreign keys
public int PersonId { get; set; }
// associations
[ForeignKey("PersonId")]
public Person Person { get; set; }
}
public class Person : BaseEntity
{
public Person()
{
....
// association
public virtual EventModerator EventModerator { get; set; }
}
My Mappings:
modelBuilder.Entity<Person>()
.HasOptional(e => e.EventModerator)
.WithRequired(e => e.Person);
This is a 1 to 0.1 relationship.
Can anyone point out my error please?
OK, this worked, but frankly I do not understand the need for ".WithMany()"
internal static void Map(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<EventModerator>()
.HasRequired(e => e.Person)
.WithMany()
.HasForeignKey(e => e.PersonId)
.WillCascadeOnDelete(false);
}
Your answer will not produce 1 to 0.1 relationship. There is another key generated on Person table in the database, that is nullable EventModerator_Id.
To have 1 to 0.1, the dependent EventModerator primary key must also be the foreign key.
You can either add [Key] attribute on PersonId.
[Key]
public int PersonId { get; set; }
Or since you have BaseEntity which might have derived Id property (which by default convention is a primary key), then you just need to remove the PersonId property and link foreign key to Id property.
//public int PersonId { get; set; }
// associations
[ForeignKey("Id")]
public Person Person { get; set; }

One to one OPTIONAL relationship

Traditional EF questions starts with: My models are
public class Ingredient
{
public int IngredientID { get; set; }
public virtual RequestedIngredient RequestedIngredient { get; set; }
// other stuff
}
public class RequestedIngredient
{
[Key]
string BlahBlahBlah { get; set; }
public int? IngredientID { get; set; }
public virtual Ingredient Ingredient { get; set; }
}
Somewhere in dbContext...
modelBuilder.Entity<Ingredient>()
.HasOptional<RequestedIngredient>(e => e.RequestedIngredient)
.WithOptionalPrincipal(e => e.Ingredient)
.Map(e => e.MapKey("IngredientID"))
.WillCascadeOnDelete(false);
But I get Schema specified is not valid. Errors:
(195,6) : error 0019: Each property name in a type must be unique. Property name 'IngredientID' was already defined.
If I remove IngredientID from RequestedIngredient, the db will be created just as I want to. But I have no access to IngredientID. How can I set this up to have access to foreign key?
This is not one-to-one relationship. In one-to-one relationship the foreign key must be a primary key. Which is not the case in this example. This is one-to-many, but I assumed that my app will take care of making sure there's only one association.
EF can deal with that using Independent Association. It will create foreign key, hidden from your POCO class. One can specify the name of the column using MapKey as I did. However, because I also created a property called IngredientID, just as the column used with MapKey, the EF has a problem as two properties are mapped to the same column.
So things like that are possible in EF, but you can't use foreign key anymore.

Mapping entities with fluent api on entity framework 5

I have a question.
I have these two tables:
The principal table is User with Customer dependence.
The reverse engineer code first generated classes as follows:
public class User
{
public User()
{
this.Customers = new List<Customer>();
}
...
public virtual ICollection<Customer> Customers { get; set; }
}
public class Customer
{
public Customer()
{
}
...
public int UserID { get; set; }
public virtual User User { get; set; }
}
I made the following modification in the user class:
public class User
{
public User()
{
}
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
}
Because the relationship is One-to–Zero-or-One.
The original mapping is this:
// Relationships
this.HasRequired(t => t.User)
.WithMany(t => t.Customers)
.HasForeignKey(d => d.UserID);
And the modified mapping is this :
this.HasRequired(t => t.User)
.WithOptional(t => t.Customer)
.Map(m => m.MapKey("UserID"));
Is That correct?
If not, how would this mapping?
Thanks.
No, it's not correct.
The best thing you can do - supposed you can change the database schema - is removing the UserID foreign key from the Customer table and then create the relationship in the database between the two primary keys so that Customer.CustomerID is the foreign key in the association.
Reverse Engineering should then automatically create the expected one-to-one relationship, like so:
public class Customer
{
public int CustomerID { get; set; }
public virtual User User { get; set; }
//...
}
public class User
{
public int UserID { get; set; }
public virtual Customer Customer { get; set; }
//...
}
//...
this.HasRequired(t => t.User)
.WithOptional(t => t.Customer);
If you can't change the database schema, your best bet is to only remove the collection ICollection<Customer> Customers from the User class and keep the relationship as one-to-many.
The reason for all this is that EF only supports shared primary key one-to-one associations, but not foreign key one-to-one associations. (The latter one you can only "fake" by removing the collection, but it's still one-to-many from EF viewpoint.)
You can read more about one-to-one associations with EF and its limitations here:
One-to-one Shared Primary Key Associations
One-to-one Foreign Key Associations