How can I define in a DB first POCO model, a field that is both PK and FK together - entity-framework

Table EMPLOYEE has MST_SQ (master-sequence) as both it's primary key, and as an FK to the primary key of table MASTER, which is also named MST_SQ. This table is used to join several other tables as well so that they all have the same PK. That is as far as my understanding goes.
I need to defined a 1 to 1 relationship in my model between class Employee and class Master, but I simply cannot find a way to do this. It seems only relationships with multiplicty allow an FK field to be speficied, and those that look like for 1 to 1, e.g. has optional(...)..WithRequiredPrincipal(....) has no FK space.
I could do some manual coding to link EMPLOYEE and MASTER when the are loaded, but how could I tell they were loaded. Is there any event that signals a POCO being populated from the DB? Or, the real question, how do I define this relationship in code?

From Relationships and Navigation Properties :
When working with 1-to-1 or 1-to-0..1 relationships, there is no
separate foreign key column, the primary key property acts as the
foreign key
From Configuring a Required-to-Optional Relationship (One-to–Zero-or-One) :
because the name of the property does not follow the convention the
HasKey method is used to configure the primary key
public class Master
{
public int MST_SQ { get; set; }
public virtual Employee Employee { get; set; }
}
public class Employee
{
public int MST_SQ { get; set; }
public virtual Master Master { get; set; }
}
The Employee has the MST_SQ property that is a primary key and a foreign key:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Master>().HasKey(m => m.MST_SQ);
modelBuilder.Entity<Employee>().HasKey(e => e.MST_SQ);
modelBuilder.Entity<Employee>()
.HasRequired(e => e.Master) //Employee is the Dependent and gets the FK
.WithOptional(m => m.Employee); //Master is the Principal
}
Generated migration code:
CreateTable(
"dbo.Employees",
c => new
{
MST_SQ = c.Int(nullable: false),
})
.PrimaryKey(t => t.MST_SQ)
.ForeignKey("dbo.Masters", t => t.MST_SQ)
.Index(t => t.MST_SQ);
CreateTable(
"dbo.Masters",
c => new
{
MST_SQ = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.MST_SQ);
So you don't need the "FK space" because EF makes it the foreign key without you having to specify it

Related

Entity Framework - error when adding entity with related entity

I have two entities:
public class EntityA
{
public int? Id { get; set; }
public string Name { get; set; }
public EntityB { get; set; }
}
public class EntityB
{
public int? Id { get; set; }
public string Version { get; set; }
}
I have existing records for EntityB already in the database. I want to add a new EntityA with reference to one of the EntityB records.
var entityB = _dbContext.EntityB.FirstOrDefault(e => e.Id == 1);
var entityA = new EntityA { Name = "Test", EntityB = entityB };
_dbContext.Add(entityA);
_dbContext.SaveChanges();
When the above code runs I get the following error:
System.InvalidOperationException: The property 'Id' on entity type 'EntityB' 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' then associate the dependent with the new principal.
This seems to me, that the save is trying to also add EntityB, not just a reference to it. I do have the relationship specified in the database as well as in Entity Framework, e.g. when querying for EntityA if I include EntityB in the select, I get the referenced entity as well (so the relationship works).
modelBuilder.Entity<EntityA>(e =>
{
e.HasKey(p => p.Id);
e.HasOne(p => p.EntityB)
.WithOne()
.HasForeignKey<EntityB>(p => p.Id);
}
modelBuilder.Entity<EntityB>(e =>
{
e.HasKey(p => p.Id);
}
How can I save a new EntityA, with only a reference to the selected EntityB, rather than saving both entities?
It looks like you are trying to Extend EntityB with an optional 1:1 reference to a Row n the new table EntityA. You want both records to have the same value for Id.
This 1:1 link is sometimes referred to as Table Splitting.
Logically in your application the record from EntityB and EntityA represent the same business domain object.
If you were simply trying to create a regular 1 : many relationship, then you should remove the HasOne().WithOne() as this creates a 1:1, you would also not try to make the FK back to the Id property.
The following advice only applies to configure 1:1 relationship
you might use Table Splitting for performance reasons (usually middle tier performance) or security reasons. But it also comes up when we need to extend a legacy schema with new metadata and there is code that we cannot control that would have broken if we just added the extra fields to the existing table.
Your setup for this is mostly correct, except that EntityA.Id cannot be nullable, as the primary key it must have a value.
public class EntityA
{
public int Id { get; set; }
public string Name { get; set; }
public EntityB { get; set; }
}
If you want records to exist in EntityA that DO NOT have a corresponding record in EntityB then you need to use another Id column as either the primary key for EntityA or the foreign key to EntityB
You then need to close the gap with the EntityA.Id field by disabling the auto generated behaviour so that it assumes the Id value from EntityB:
modelBuilder.Entity<EntityA>(e =>
{
e.HasKey(p => p.Id).ValueGeneratedNever();
e.HasOne(p => p.EntityB)
.WithOne()
.HasForeignKey<EntityB>(p => p.Id);
}
I would probably go one step further and add the Reciprocating or Inverse navigation property into EntityB this would allow us to use more fluent style assignment, instead of using _dbContext.Add() to add the record to the database:
public class EntityB
{
public int Id { get; set; }
public string Version { get; set; }
public virtual EntityA { get; set; }
}
With config:
modelBuilder.Entity<EntityA>(e =>
{
e.HasKey(p => p.Id).ValueGeneratedNever();
e.HasOne(p => p.EntityB)
.WithOne(p => p.EntityA)
.HasForeignKey<EntityB>(p => p.Id);
}
This allows you to add in a more fluent style:
var entityB = _dbContext.EntityB.FirstOrDefault(e => e.Id == 1);
entityB.EntityA = new EntityA { Name = "Test" };
_dbContext.SaveChanges();
This will trip up because you are using EntityA's PK as the FK to Entity B, which enforces a 1 to 1 direct relation. An example of this would be to have something like an Order and OrderDetails which contains additional details about a specific order. Both would use "OrderId" as their PK and OrderDetails uses it's PK to relate back to its Order.
If instead, EntityB is more like an OrderType reference, you wouldn't use a HasOne / WithOne relationship because that would require Order #1 to only be associated with OrderType #1. If you tried linking OrderType #2 to Order #1, EF would be trying to replace the PK on OrderType, which is illegal.
Typically the relationship between EntityA and EntityB would require an EntityBId column on the EntityA table to serve as the FK. This can be a property in the EntityA entity, or left as a Shadow Property (Recommended where EntityA will have an EntityB navigation property) Using the above example with Order and OrderType, an Order record would have an OrderId (PK) and an OrderTypeId (FK) to the type of order it is associated with.
The mapping for this would be: (Shadow Property)
modelBuilder.Entity<EntityA>(e =>
{
e.HasKey(p => p.Id);
e.HasOne(p => p.EntityB)
.WithMany()
.HasForeignKey("EntityBId");
}
An OrderType can be assigned to many Orders, but we don't have an Orders collection on OrderType. We use the .HasForeignKey("EntityBId") to set up the shadow property of "EntityBId" on our EntityA table. Alternatively, if we declare the EntityBId property on our EntityA:
modelBuilder.Entity<EntityA>(e =>
{
e.HasKey(p => p.Id);
e.HasOne(p => p.EntityB)
.WithMany()
.HasForeignKey(p => p.EntityBId);
}
On a side note, navigation properties should be declared virtual. Even if you don't want to rely on lazy loading (recommended) it helps ensure the EF proxies for change tracking will be fully supported, and lazy loading is generally a better condition to be in at runtime than throwing NullReferenceExceptions.

configure related entities with the same key

I have an entity type MyEntity that has a primary key string MyEntityCode
I want to make a second entity MyEntityInfo that are extended properties that some MyEntity's are logically associated.
That makes the relationship between these entities one-to-one, with one end optional -- MyEntity logically optionally has a MyEntityInfo, without a navigation property, and MyEntityInfo is required to have a single MyEntity (with a navigation property).
I want to encode this in SQL as MyEntityInfo having a primary key BaseEntityCode that's also a foreign key to MyEntity's MyEntityCode.
How do I configure this encoding in EF6 fluent configuration API.
Sample code
public class MyEntity {
public string MyEntityCode {get; set;}
public int SomeProperty {get; set;}
}
public class MyEntityInfo {
public MyEntity BaseEntity {get; set;}
public string BaseEntityCode {get; set;}
public int OtherInfo {get; set;}
}
public MyEntityConfiguration : EntityConfiguration<MyEntity> {
public MyEntityConfiguration(){
HasKey(e => e.MyEntityCode);
}
}
I thought I could configure MyEntityInfo as
public MyEntityInfoConfiguration : EntityConfiguration<MyEntityInfo> {
public MyEntityInfoConfiguration(){
HasKey(e => e.BaseEntityCode);
HasRequired(e => e.BaseEntity).WithOptional().WithForeignKey(e => BaseEntityCode);
}
}
but WithOptional() doesn't allow chaining to WithForeignKey
Doing the same but with WithMany() so that a foreign key is possible, the multiplicity constraint of one is violated:
Because the Dependent Role refers to the key properties, the upper bound of the multiplicity of the Dependent Role must be '1'.
I thought I could configure MyEntityInfo as
public class MyEntityInfoConfiguration : EntityTypeConfiguration<MyEntityInfo>
{
public MyEntityInfoConfiguration(){
HasKey(e => e.BaseEntityCode);
HasRequired(e => e.BaseEntity).WithOptional().WithForeignKey(e => BaseEntityCode);
}
}
Well, almost, just remove the WithForeignKey call!
public class MyEntityInfoConfiguration : EntityTypeConfiguration<MyEntityInfo>
{
public MyEntityInfoConfiguration()
{
HasKey(e => e.BaseEntityCode);
HasRequired(e => e.BaseEntity).WithOptional();
}
}
Entity Framework 6 has only one implementation of 1:1 associations: the primary key of the dependent entity (here: MyEntityInfo) is the foreign key to the principal entity (here: MyEntity).
There is no WithForeignKey method because with your proposed mapping (without WithForeignKey) EF knows all it needs to know now for the only implementation of 1:1 it has in store.
The produced database model shows the primary key/foreign key dual role of BaseEntityCode:
CREATE TABLE [dbo].[MyEntities] (
[MyEntityCode] [nvarchar](128) NOT NULL,
[SomeProperty] [int] NOT NULL,
CONSTRAINT [PK_dbo.MyEntities] PRIMARY KEY ([MyEntityCode])
)
CREATE TABLE [dbo].[MyEntityInfoes] (
[BaseEntityCode] [nvarchar](128) NOT NULL,
[OtherInfo] [int] NOT NULL,
CONSTRAINT [PK_dbo.MyEntityInfoes] PRIMARY KEY ([BaseEntityCode])
)
...
ALTER TABLE [dbo].[MyEntityInfoes]
ADD CONSTRAINT [FK_dbo.MyEntityInfoes_dbo.MyEntities_BaseEntityCode]
FOREIGN KEY ([BaseEntityCode]) REFERENCES [dbo].[MyEntities] ([MyEntityCode])

Entity Framework: one to zero or one relationship with foreign key on principal

I have a 1:0..1 relationship that I'd like to map with EF 6 using fluent API. The relation consists of a principal, which may or may not have a dependent. A dependent must always have a principal.
In the principal, I need to have access to the Id of the dependent.
My code looks like this:
public class Principal
{
public int Id {get; private set; }
public int? DependentId { get; private set; }
public virtual Dependent Dependent { get; private set; }
}
public class Dependent
{
public int Id { get; private set; }
public virtual Principal Principal { get; private set; }
}
My mapping looks like this:
public class PrincipalMap : EntityTypeConfiguration<Principal>
{
public PrincipalMap()
{
ToTable("PRINCIPALS");
HasKey(x => x.Id);
Property(x => x.Id)
.HasColumnName("PRINCIPALID")
.IsRequired()
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(x => x.DependentId)
.HasColumnName("DEPENDENTID")
.IsOptional();
}
}
public class DependentMap : EntityTypeConfiguration<Dependent>
{
public DependentMap()
{
ToTable("DEPENDENTS");
HasKey(x => x.Id);
Property(x => x.Id)
.HasColumnName("DEPENDENTID")
.IsRequired()
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(x => x.Principal).WithOptional(x => x.Dependent).Map(x => x.MapKey("PRINCIPALID")).WillCascadeOnDelete();
}
}
Which results in the following migration:
CreateTable(
"dbo.PRINCIPALS",
c => new
{
PRINCIPALID = c.Int(nullable: false, identity: true),
DEPENDENTID = c.Int(),
})
.PrimaryKey(t => t.PRINCIPALID);
CreateTable(
"dbo.DEPENDENTS",
c => new
{
DEPENDENTID = c.Int(nullable: false, identity: true),
PRINCIPALID = c.Int(nullable: false),
})
.PrimaryKey(t => t.DEPENDENTID)
.ForeignKey("dbo.PRINCIPALS", t => t.PRINCIPALID, cascadeDelete: true)
.Index(t => t.PRINCIPALID);
As you can see, the column DEPENDENTID is not a foreign key. When running the program and associating a dependent object to a principal, the DependentId property remains empty, i.e. EF does not recognize it to be related to the dependent itself.
What am I doing wrong?
In DependentMap you declared field DEPENDENTID as primary key of DEPENDENT table, database generated (identity) so it will never be a foreign key. You can't change it as you want (making it pointing to an entity of your choice).
Also, with EF (and E/R) you don't need two columns (one per table) to have a 1-0..1 relationship. You can have only one column (not nullable).
In your case this model should work:
public class Principal
{
public int Id { get; private set; }
public virtual Dependent Dependent { get; private set; }
}
public class Dependent
{
public int Id { get; private set; }
public virtual Principal Principal { get; private set; }
}
public class PrincipalMap : EntityTypeConfiguration<Principal>
{
public PrincipalMap()
{
ToTable("PRINCIPALS");
HasKey(x => x.Id);
Property(x => x.Id)
.HasColumnName("PRINCIPALID")
.IsRequired()
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
public class DependentMap : EntityTypeConfiguration<Dependent>
{
public DependentMap()
{
ToTable("DEPENDENTS");
HasKey(x => x.Id);
Property(x => x.Id)
.HasColumnName("DEPENDENTID")
.IsRequired()
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(x => x.Principal).WithOptional(x => x.Dependent).Map(x => x.MapKey("PRINCIPALID")).WillCascadeOnDelete();
}
}
In this case the table creation stataments (generated by EF provider) should be similar to
ExecuteNonQuery==========
CREATE TABLE [DEPENDENTS] (
[DEPENDENTID] int not null identity(1,1)
, [PRINCIPALID] int not null
);
ALTER TABLE [DEPENDENTS] ADD CONSTRAINT [PK_DEPENDENTS_204c4d57] PRIMARY KEY ([DEPENDENTID])
ExecuteNonQuery==========
CREATE TABLE [PRINCIPALS] (
[PRINCIPALID] int not null identity(1,1)
);
ALTER TABLE [PRINCIPALS] ADD CONSTRAINT [PK_PRINCIPALS_204c4d57] PRIMARY KEY ([PRINCIPALID])
ExecuteNonQuery==========
CREATE INDEX [IX_PRINCIPALID] ON [DEPENDENTS] ([PRINCIPALID])
ExecuteNonQuery==========
ALTER TABLE [DEPENDENTS] ADD CONSTRAINT [FK_DEPENDENTS_PRINCIPALS_PRINCIPALID] FOREIGN KEY ([PRINCIPALID]) REFERENCES [PRINCIPALS] ([PRINCIPALID])
(I omitted on cascade delete but should be clear as well).
The E/R model is in normal form (and is the only that works with EF).
BTW, if you access to Principal.Dependent property EF will generate a query similar to selected * from dependent where PRINCIPALID = <principal_id> where is the id of the principal entity so it really works.
Now, about your requirements, to access to Dependent.Id from Principal the only way is dependentId = Principal.Dependent.Id (or, better, dependentId = Principal.Dependent == null ? null : Principal.Dependent.Id).
What to do if you REALLY WANT a field for the foreign key on PRINCIPAL that refers to DEPENDENT table?
This model is not in normal form so EF will not handle it (also with DBMS you need to write triggers to handle it).
I mean, in R-DBMS there is not a constraint where you can specify that if a column DEPENDENT.PRINCIPALID refers to a PRINCIPAL also a column PRINCIPAL.DEPENDENTID should refers to the original DEPENDENT.
What you need to do in this case is to handle PRINCIPAL.DEPENDENTID yourself (i.e. the Principal entity must have a DEPENDENTID property that you must handle by yourself and is not used by EF during navigation).
Yes, that is tricky and an EF bug IMO. The workaround I have used is a pseudo 1:M:
HasRequired(x => x.Principal)
.WithMany()
.HasForeignKey(x => x.DependentId);
.WillCascadeOnDelete();
http://weblogs.asp.net/manavi/associations-in-ef-4-1-code-first-part-5-one-to-one-foreign-key-associations

EF Code First creating redundant nonclustered index for 1-to-0..1 relationship

Consider the following entities with a 1:0..1 relationship, where a Foo may have a related FooBar, and FooBar.Id is both the primary key and a foreign key to Foo.Id:
public class Foo
{
public int Id {get; set;}
public virtual FooBar FooBar {get; set;}
}
public class FooBar
{
public int Id {get; set;}
}
modelBuilder.Entity<Foo>().HasOptional(e => e.FooBar).WithRequired();
This works well, except the generated migration code in Up() contains a redundant index on FooBar.Id:
CreateTable(
"dbo.FooBar",
c => new
{
Id = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Foo", t => t.Id)
.Index(t => t.Id);
As you can see, the call to PrimaryKey will create a clustered index, and there's an additional Index call scaffolded from the foreign key. Indeed in the database, I see both indexes. Correct me if I'm wrong, but the nonclustered index will never be used in this scenario and its only function would be to simply take up space.
I assume this is happening because the foreign key in the framework has no knowledge of the primary key. So, is there any way to get EF to realize the column already has an index from the primary key and not scaffold an additional index from the foreign key (without manually modifying the code in the migration's Up() method and removing the .Index() call)?

Modeling a composite key of foreign keys (modelled as entity references) in EF6

I am coming from nHibernate and am trying to create an entity that has a 2 column composite key where both columns are also foreign keys.
For example I have a UserRole table that is (UserId Guid, RoleId Guid). I want to model this as
public class UserRole
{
public User User { get; set; }
public Role Role { get; set; }
}
EF doesn't seem to like this idea though. It seems to want me to also add Guid UserId {get;set;} and Guid RoleId { get; set; }. I managed to resolve this for the handling the FK part by defining the model in the dbcontext like so:
modelBuilder.Entity<UserRole>()
.HasRequired(x => x.Role)
.WithRequiredPrincipal()
.Map(x => x.MapKey("RoleId"));
modelBuilder.Entity<UserRole>()
.HasRequired(x => x.User)
.WithRequiredPrincipal()
.Map(x => x.MapKey("UserId"));
Which I hope I can turn into a convention. However when I tried to do this too:
modelBuilder.Entity<UserRole>().HasKey(x => new { x.User, x.Role });
it craps out at runtime with:
The property 'User' cannot be used as a key property on the entity 'Paroxysm.Domain.UserRole' because the property type is not a valid key type. Only scalar types, string and byte[] are supported key types.
FYI this is done in nHibernate byCode mapping like this (slightly different example):
public class ProjectUserProfileMap : ClassMap<ProjectUserProfile>
{
public ProjectUserProfileMap()
{
CompositeId()
.KeyReference(x => x.User, "UserId")
.KeyReference(x => x.Project, "ProjectId");
ReadOnly();
References(x => x.User, "UserId");
References(x => x.Project, "ProjectId");
Map(x => x.IsActive);
Map(x => x.ActivatedUtcDate).Not.Nullable().CustomType<NHibernate.Type.UtcDateTimeType>();
Map(x => x.InvitedUtcDate).Not.Nullable().CustomType<NHibernate.Type.UtcDateTimeType>();
Table("ProjectUserProfile");
}
}
So easy! Incidentally that little CustomType UTC behaviour doesn't seem to be supported by EF either.
Problem is not actually related to the fact that I have a composite key but having a single column PK which is also an FK would be a weird case (1:1 rel).
So I guess I want to know definitely if this can or cannot be done in EF6. The error message certainly indicates its not doable. Can someone confirm?
You could achieve this but only if you add to UserRole 2 foreign key properties: RoleId and UserId. This is because HasKey do not offer any mapping functionality - it can be defined only on properties existing in entity classes. It seems EF enforces that all Primary Key columns are always defined as concrete properties in classes wheres foreign key columns may not have corresponding properies defined. So to achieve what you want you need to define UserRole like this:
public class UserRole
{
public User User { get; set; }
public int UserId { get; set; }
public Role Role { get; set; }
public int RoleId { get; set; }
}
modelBuilder.Entity<UserRole>()
.HasRequired(x => x.Role)
.WithRequiredPrincipal()
.HasForeignKey(x => x.RoleId);
modelBuilder.Entity<UserRole>()
.HasRequired(x => x.User)
.WithRequiredPrincipal()
.HasForeignKey(x => x.UserId);
modelBuilder.Entity<UserRole>().HasKey(x => new { x.UserId, x.RoleId });
The exact situation as you posted you might alternatively achieve by many-to-many relationship:
modelBuilder.Entity<User>()
.HasMany(x => x.Roles)
.WithMany()
.Map(m =>
{
m.ToTable("UserRole");
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
});
With this you would achieve UserRole table with primary key defined on UserId and RoleId.