I am using entity framework 4, mvc4 and code first.
I'm struggling with creating an option 1:1 mapping, where the main entity that has the optional 1:1 mapping doesn't have the FK in it:
public class User
{
[Column("user_id")]
public int Id {get;set;}
public virtual House House {get;set;} // optional mapping
}
public class House
{
[Column("house_id")]
public int Id {get;set;}
[Column("user_id")]
public int UserId {get;set;}
}
Notice how the user table doesn't have teh houseId column.
How can I map this correctly?
Note: the below method isn't what i really want to do since it forces me to add a navigational property on the House model also back to User.
I tried this method, although I had to add a virtual property to the House model which I didn't want to do: How do I code an optional one-to-one relationship in EF 4.1 code first with lazy loading and the same primary key on both tables?
So my configuration looks like with the above attempt:
public class UserConfiguration : EntityTypeConfiguration<User>
{
public UserConfiguration()
{
this.ToTable("User", SchemaName);
this.HasKey(x => x.Id);
this.HasOptional(x => x.House);
}
}
public class HouseConfiguration : EntityTypeConfiguration<House>
{
public HouseConfiguration()
{
this.ToTable("House", SchemaName);
this.HasKey(x => x.Id);
this.HasRequired(vc => vc.User).WithRequiredDependent(v => v.House);
}
}
But when I do this, saving the model I get this error:
Cannot insert explicit value for identity column in table 'House' when IDENTITY_INSERT is set to OFF
Note: without the above setup (mapping and configuration), the House entity saves just fine to the database and the identity is setup correctly.
Remove the UserId property from House, remove the this.HasRequired... mapping from the HouseConfiguration constructor and use in UserConfiguration:
this.HasOptional(x => x.House).WithRequired();
This will define a shared primary key association (i.e. House.Id is primary key of House and the foreign key to User at the same time).
If you have an existing database with a separate foreign key column user_id in the House table and this column has a unique key constraint to enforce a one-to-one relation in the database you cannot map this as one-to-one relationship with Entity Framework because EF doesn't support foreign key one-to-one associations.
You would have to map this as one-to-many relationship in this case and unfortunately you can't have a single reference House on the principal User. You would have to use a collection of Housees instead (and ensure in business logic that you never add more than one element to this collection, otherwise upon saving you would get an exception due to a violation of the unique FK constraint in the House table) or no navigation property at all in User. But then you need a navigation reference User in entity House so that EF would be able to map a relationship at all (at least a navigation property on one side of a relationship is always needed).
Can't rollback to EF4, but was only using it in a similar way a while ago, so don't believe this has changed.
You need to set a Key on the House object and set it to DB generated:
using System.ComponentModel.DataAnnotations.Schema
using System.ComponentModel.DataAnnotations
...
public class House
{
[Column("house_id")]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Column("user_id")]
public int UserId { get; set; }
}
Related
I have two entities with a relationship without a key, when I want to edit the personnel code field in Personnel, this error is displayed to me.
'The property 'Personnel.PersonnelCode' is part of a key and so cannot be modified or marked as modified. To change the principal of an existing entity with an identifying foreign key, first delete the dependent and invoke 'SaveChanges', and then associate the dependent with the new principal.'
The EnrolNumberPersonnelNum table also does not have a record that has a relationship with the changed personnel record in my table
personnel class
public class Personnel : BaseEntity, IBaseEntityTypeConfiguration<Personnel>
{
public Personnel()
{
EnrolNumberPersonnelNums = new HashSet<EnrolNumberPersonnelNum>();
}
[Key]
public int PersonnelId { get; set; }
public string PersonnelCode { get; set; }
public virtual ICollection<EnrolNumberPersonnelNum> EnrolNumberPersonnelNums { get; set; }
public void Map(EntityTypeBuilder<Personnel> builder)
{
builder.ToTable("Personnel", "HR");
builder.Property(e => e.PersonnelCode).HasMaxLength(50);
}
}
EnrolNumberPersonnelNum class
public class EnrolNumberPersonnelNum : BaseEntity, IBaseEntityTypeConfiguration<EnrolNumberPersonnelNum>
{
public EnrolNumberPersonnelNum()
{
}
[Key]
public int EnrolNumberPersonnelNumId { get; set; }
public string PersonnelCode { get; set; }
public virtual Personnel Personnel { get; set; }
public void Map(EntityTypeBuilder<EnrolNumberPersonnelNum> builder)
{
builder.ToTable("EnrolNumberPersonnelNum", "HR");
builder.Property(e => e.PersonnelCode)
.HasMaxLength(50);
builder.HasOne(c => c.Personnel).WithMany(c => c.EnrolNumberPersonnelNums)
.HasForeignKey(c => c.PersonnelCode).HasPrincipalKey(c => c.PersonnelCode)
.OnDelete(DeleteBehavior.ClientNoAction)
.HasConstraintName($"FK_{nameof(EnrolNumberPersonnelNum)}_{nameof(Personnel)}_{nameof(PersonnelCode)}");
}
}
Error message when I want to edit personnel code in personnel entity
and EnrolNumberPersonnelNum table is empty .
HasPrincipalKey(c => c.PersonnelCode) here
builder.HasOne(c => c.Personnel).WithMany(c => c.EnrolNumberPersonnelNums)
.HasForeignKey(c => c.PersonnelCode).HasPrincipalKey(c => c.PersonnelCode)
.OnDelete(DeleteBehavior.ClientNoAction)
.HasConstraintName($"FK_{nameof(EnrolNumberPersonnelNum)}_{nameof(Personnel)}_{nameof(PersonnelCode)}");
marks the property PersonnelCode of the Personnel entity as alternate key:
An alternate key serves as an alternate unique identifier for each entity instance in addition to the primary key; it can be used as the target of a relationship. When using a relational database this maps to the concept of a unique index/constraint on the alternate key column(s) and one or more foreign key constraints that reference the column(s).
And in EF Core all type of keys (primary and alternate) are read only, i.e. EF Core does not allow updating them (are required and can be provided only for new entities). This is basically what the error message is telling you at the beginning
The property 'Personnel.PersonnelCode' is part of a key and so cannot be modified or marked as modified.
With that being said, there is no solution so far for such type of model, if you need that property editable. The suggestion in the error message
To change the principal of an existing entity with an identifying foreign key, first delete the dependent and invoke 'SaveChanges', and then associate the dependent with the new principal.
doesn't seem viable, since obviously you cannot delete dependents (EnrolNumberPersonnelNums in you case) and then associate them with a new parent (since they are deleted). Eventually you can try loading them in memory, then call RemoveRange, modify the parent key, then SaveChanges, then update PersonnelCode of the cached children instances, call AddRange followed by SaveChanges. Looks unreliable/error prone, but worth trying.
If you are allowed to modify the database, better remove the PersonnelCode from EnrolNumberPersonnelNum and create and use regular FK int PersonnelId bound to the PK of the Personnel. This way you can remove the alternate key (and add just unique constraint if needed) as mentioned in the documentation
Tip
If you just want to enforce uniqueness on a column, define a unique index rather than an alternate key (see Indexes). In EF, alternate keys are read-only and provide additional semantics over unique indexes because they can be used as the target of a foreign key.
That will allow updating the PersonnelCode of Personel as any other property.
Assume I have following database tables with 1:1 mapping
Table dbo.Foo with PrimaryKey FooRowId
Table dbo.Bar with PrimaryKey BarRowId
No foreign keys exist on either table.
Using EF, I defined models as follows.
Modeling Foo table
public class Foo
{
[Key]
public long FooRowId { get; set; }
// Navigation
public virtual Bar Bar { get; set; }
}
Modeling Bar table
public class Bar
{
[Key]
public long BarRowId { get; set; }
// Navigation
public virtual Foo Foo { get; set; }
}
This gives me navigation property related error as follows.
Unable to determine the principal end of an association between the types 'MyOrg.Models.Foo'
and 'MyOrg.Models.Bar'. The principal end of this association must be explicitly
configured using either the relationship fluent API or data annotations.
One way to fix is by standardizing property names as follows.
On Foo
public class Foo
{
[Key]
[Column("FooRowId")]
public long FooId { get; set; }
// Navigation
public virtual Bar Bar { get; set; }
}
On Bar
public class Bar
{
[Key]
[Column("BarRowId")]
public long BarId { get; set; }
// Navigation
public virtual Foo Foo { get; set; }
}
However, requirement states I must keep the original properties FooRowID and BarRowId. Given this constraint, how to make the navigation properties work?
Although Attributes seem easy to use, it limits reuse of your classes in other databases.
Suppose you have created a class BillingAddress according to the standards of your company (country maybe). You want to use this class in two different DbContexts, each to represent their own database. The BillingAddress in database 1 has a primary key in column "MyPrimaryKey", the BillingAddress in database 2 has a primary key in column "Id". You can't solve that using attributes.
The names of columns and tables, the relation between tables is part of the database. Therefore this ought to be described in the DbContext. If you don't use Attributes in the DbSet classes, you'll be able to use the same class in different databases.
So let's write your table design in fluent Api
See:
Fluent API to configure tables, like table name
Fluent API to configure properties, like primary key, column names, foreign keys
Fluent API to configure relations between tables, like one-to-many
Below I give examples for all three categories:
Configure the table name and the primary key of the table
Configure one of the properties: column name
Configure relation between tables, for example one-to-many
Keep in mind: if you use code first, and you stick to the entity framework code first conventions, none of this is needed. As long as you stick to these conventions, Entity Framework will be very capable in detecting primary keys, foreign keys, relations between tables.
But if your tables are different, the following fluent API is needed in your DbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure entity table FOO:
var entityFoo = modelBuilder.Entity<Foo>();
// example: configure Name and primary key of foo table
entityFoo.ToTable("MySpecialFooTable");
entifyFoo.HasKey(foo => foo.FooRowId);
// example: configure property Name is in column FooName:
entityFoo.Property(foo => foo.Name).HasColumnName("MyFooName");
// example: one-to-many relation between foo and bar
// every Foo has zero or more Bars in property MyBars,
// every Bar belongs to exactly one For in property MyFoo
// using foreign key MyFooId:
entityFoo.HasMany(foo => foo.MyBars) // Foo has zero or more Bars in MyBars
.WithRequired(bar => bar.MyFoo) // every Bar belongs to one Foo
.HasForeignKey(bar => bar.MyFooId); // using foreign key MyFooId
}
In my User model I need an optional constraint to an active playlist.
public class User
{
//.. Properties
public int? ActivePlaylistID { get; set; }
public virtual Playlist ActivePlaylist { get; set; }
}
In my database i have ActivePlaylistID set to nullable with a relationship established as: 'ActivePlaylistID' is a foreign key on table 'Playlists', column 'ID'.
In my Playlist model i have:
public class Playlist : BaseModel
{
//.. Properties
public int CreatedByUserID { get; set; }
public virtual User CreatedByUser { get; set; }
}
The CreatedByUserID relationship is established in my database and the model property is set in my controller before saving a new Playlist.
I get the following error at this point in the setup:
Unable to determine the principal end of an association between the types 'SyncFinder.Models.Playlist' and 'SyncFinder.Models.User'.
The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
So in my DbEntity i added the following to my model builder:
modelBuilder.Entity<User>()
.HasOptional(x => x.ActivePlaylist)
.WithRequired(x => x.CreatedByUser);
At this point the view loads, but when trying to add a new playlist to my database i get:
A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'ID'.
I am not sure how to resolve this constraint issue and the stacktrace isn't providing much detail except that the operation fails when trying to save the playlists repository.
Only one half of the relation was bound in the model binder. I am uncertain of the full support for one-to-one relations in Entity Framework, but for your example completing the relation should work
modelBuilder.Entity<Playlist>().HasKey(e => e.CreatedByUserID);
modelBuilder.Entity<User>()
.HasOptional(x => x.ActivePlaylist)
.WithRequired(x => x.CreatedByUser);
I am using Entity Framework and Code First to map to a legacy database (I know that if I have the database already built then I should the "Database First" approach, but some how, I fill better putting the annotations by hand than handling control over the designer), in which, there is a table that have a key that point to another table (it is actually a synonym to a table in another db) that works like a foreign key but it not actually defined in the table schema.
The question is: can I force this relation to exist in my POCO classes? can I map this using the Fluent API? is this possible at all?
As usual, thanks in advance!
Sure, you can manually define a foreign key and navigation properties either by fluent mapping or data annotations.
Let's say I have a Parent table with a field CatCode and a synonym CategoryEx that points to a table Category in a second database. Both have corresponding classes and DbSets in one context. The mappings (derived from EntityTypeConfiguration) could look like (the constructors):
public ParentMap()
{
this.HasKey(t => t.Id);
this.HasRequired(p => p.Category).WithMany().HasForeignKey(p => p.CatCode);
...
}
public CategoryMap()
{
this.HasKey(t => t.CatCode);
this.ToTable("CategoryEx");
...
}
where Parent looks like:
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public string CatCode { get; set; }
...
public Category Category { get; set; }
}
I'm using entity framework code-first to create my database schema automatically, and one of my entities looks like this:
public class AssessmentsCaseStudies {
#region Persisted fields
[Required]
[Key, Column(Order=0)]
[ForeignKey("Assessment")]
public int AssessmentId { get; set; }
[Required]
[Key, Column(Order=1)]
[ForeignKey("CaseStudy")]
public int CaseStudyId { get; set; }
[Required]
public int Score { get; set; }
[ForeignKey("Follows")]
public int? FollowsCaseStudyId { get; set; }
#endregion
#region Navigation properties
public virtual Assessment Assessment { get; set; }
public virtual CaseStudy CaseStudy { get; set; }
public virtual CaseStudy Follows { get; set; }
#endregion
}
When EF auto-generates my database, it generates a table with the following columns:
AssessmentId (PK, FK, int, not null)
CaseStudyId (PK, FK, int, not null)
Score (int, not null)
FollowsCaseStudyId (FK, int, null)
CaseStudy_CaseStudyId (FK, int, null)
This is all fine apart from the CaseStudy_CaseStudyId column. Why has that been generated? What is it for? How can I stop it being generated? My suspicion is that EF can no longer automatically match up CaseStudy's ICollection<AssessmentsCaseStudies> with the CaseStudyId column, so it creates its own column to link the two together for that navigation property.
Because you have two navigation properties of type CaseStudy in your AssessmentsCaseStudies entity and an AssessmentsCaseStudies collection in your CaseStudy entity EF cannot decide which of the two CaseStudy navigation properties this collection refers to. Both could be possible and both options would result in a valid but different entity model and database schema.
In such an ambiguous situation the EF convention is to create actually three relationships, i.e. your collection in CaseStudy does not refer to any of the two CaseStudy navigation properties but has a third (but not exposed and "invisible") endpoint in AssessmentsCaseStudies. This third relationship is the reason for the third foreign key your are seeing in the database - the one with the underscore. (The underscore is always a strong indication that something happend by mapping convention and not by your explicit configuration or data annotations.)
To fix the problem and to override the convention you can apply the [InverseProperty] attribute, thereby specifying the CaseStudy navigation property the AssessmentsCaseStudies collection belongs to:
[InverseProperty("AssessmentsCaseStudies")] // the collection in CaseStudy entity
public virtual CaseStudy CaseStudy { get; set; }
You can also (alternatively, you don't need both) put the attribute on the collection side:
[InverseProperty("CaseStudy")] // the CaseStudy property in AssessmentsCaseStudies entity
public virtual ICollection<AssessmentsCaseStudies> AssessmentsCaseStudies { get; set; }
For some reason, Slauma's InverseProperty attribute suggestion didn't work. What did work was me specifying the relationship between the two CaseStudy navigation properties in AssessmentsCaseStudies, and the CaseStudy entity, via the Fluent API in my database context's OnModelCreating method:
modelBuilder.Entity<AssessmentsCaseStudies>()
.HasRequired(acs => acs.CaseStudy)
.WithMany(cs => cs.AssessmentsCaseStudies)
.HasForeignKey(acs => acs.CaseStudyId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<AssessmentsCaseStudies>()
.HasOptional(acs => acs.Follows)
.WithMany() // No reverse navigation property
.HasForeignKey(acs => acs.FollowsCaseStudy)
.WillCascadeOnDelete(false);
Once that's added, the migration code that's generated when I Add-Migration no longer tries to add the CaseStudy_CaseStudyId column and I just get the FollowsCaseStudyId column added, with the appropriate foreign key relationship.
For anyone else landing here looking for a solution, if you've tried the previous answers and are still getting an extra foreign key column, look for any properties you may have defined further down your POCO class that you did not intend to map to DB fields. Even if they contain code blocks, as with complex get accessors, Entity Framework will try to map them to the database somehow. This may result in extra foreign key columns if your properties return entities. To be safe, either decorate such properties with the [NotMapped] attribute or convert them to methods.