Entity Framework inheritance and relationship - c#

I'm trying to implement a relationship(one-to-many) for my entities which use default TPH inheritance
public abstract class base
{
public int Id { get; set; }
...
}
public class X : base
{
public ApplicationUser User { get; set; }
...
}
public class Y : base
{
public ApplicationUser User { get; set; }
...
}
public class ApplicationUser
{
public string Name { get; set;}
...
public ICollection<X> classX { get; set; }
public ICollection<Y> classY { get; set; }
}
Everything works, but the problem is that Entity Framework creates two columns in the base table - User_Id and User_Id1. How can I map it so that there is only one column for the foreign key (User_Id) and depending on the content of the record in the Discriminator column (created by EF) the foreign key would be assigned to the appropriate entity?

How can I map it so that there is only one column for the foreign key (User_Id) and depending on the content of the record in the Discriminator column (created by EF) the foreign key would be assigned to the appropriate entity?
You can't. Thinking about it a bit I don't see any obvious reason why that feature couldn't be implemented. It just hasn't.
If you want X and Y to share an attribute, derive them both from and intermediate XYEntity type, and give ApplicationUser a single Navigation Property of type ICollection<XYEntity>.

Related

Mapping 1-0..1 Relationship with Navigation Property Without FK

I've got 2 entities with a 1-0..1 relationship between them, but restrictions on what the generated DB schema can look like.
So 1 Vehicle to 0 or 1 RecVehicle entity
I need to be able to have a navigation property from Vehicle to RecVehicle, but without the DB Schema for the Vehicles table having a FK to RecVehicle. The PK of the RecVehicle table should be the Id of the Vehicle entity it relates to.
We are using EF code first
public class Vehicle
{
[Key]
public int Id { get; set; }
public virtual RecVehicle RecVehicle { get; set; } // Need to be able to use as navigation
}
public class RecVehicle
{
[Key]
public int VehicleId { get; set; }
[ForeignKey("VehicleId")]
public Vehicle Vehicle { get; set; }
}
The generated schema needs to be something like this:
Vehicles
[ Id(int, pk, not null), ...] <-- no FK column to RecVehicles
RecVehicles
[ VehicleId(int, pk, fk, not null), ...]
Originally what I had tried something like this:
public class Vehicle
{
[Key]
public int Id { get; set; }
[InverseProperty("Vehicle")]
public virtual RecVehicle RecVehicle { get; set; } // Need to be able to use as navigation
}
but this causes this exception:
Unable to determine the principal end of an association between the types 'Contract.Entities.Vehicle' and 'Contract.Entities.RecVehicle'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
I'm not sure what fluent API relationships to setup to make this work, nor the correct set of data annotations to make this work, or if it's even possible.
Reasoning
The reason there is strict limitations on the DB schema is our Data team has a migration/data import process that we can not alter
We have an existing code base that uses the navigation property in many places (2 teams, desync in schema) so changing to use a lookup in code requires many changes in the code base that we are trying to avoid.
The RecVehicle can be connected to multiple Vehicles
Can you try the following navigation property?
public virtual ICollection<RecVehicle> RecVehicle { get; set; }
instead of
public virtual RecVehicle RecVehicle { get; set; }
Due to the RecVehicle primary key this list only maximum contains one element
Ended up being able to get this relationship to work like this:
public class Vehicle
{
[Key]
public int Id { get; set; }
public virtual RecVehicle RecVehicle { get; set; }
}
public class RecVehicle
{
[Key]
public int VehicleId { get; set; }
[ForeignKey("VehicleId"), Required] //<--- Required attr fixed the principal/dependent confusion EF was having
public virtual Vehicle Vehicle { get; set; }
}

Entity Framework Custom Navigational Properties

Is there a way to map below ClassA.BId to ClassB.BetaId ?
BetaId in ClassB is not primary key. Thus, mapping in following way end up in "The ForeignKeyAttribute is not valid" exception. Note that there is no foreign key relationship in these 2 classes. For some reason I must not map ClassA.BId to ClassB.Id because these 2 field is unrelated but I need to custom map ClassA.BId to ClassB.BetaId due to these 2 field is related. However, The Id in ClassB must remain as primary key.
Note: I'm using Entity Framework 6
[Table("A")]
public class ClassA{
[Key]
public int Id { get; set; }
public int BId { get; set; }
[ForeignKey("BId")]
public virtual B B { get; set; }
}
[Table("B")]
public class ClassB{
[Key]
public int Id { get; set; }
public int BetaId { get; set; }
}
If B.BetaID is unique you can declare it to be the Key. Otherwise EF Core supports Foreign Key properties referencing Alternate Keys. See https://learn.microsoft.com/en-us/ef/core/modeling/alternate-keys

Entity Framework foreign key to multiple tables/entities

I need to implement Entity-Attribute-Value functionality on multiple data tables using Entity Framework. Let's say I have an attribute value EF class that looks like this:
public class EntityAttributeValue
{
// Not important to my question.
public virtual Entity ParentEntity { get; set; }
public virtual EntityAttribute ParentEntityAttribute { get; set; }
// Field in question.
public Guid ParentSurrogateKey { get; set; }
public string Value { get; set; }
...
}
Then I have multiple entities that have supplementary EAV values associated with them:
public class Entity1
{
// Key. EntityAttributeBalue.ParentSurrogateKey maps to this.
[Key]
public Guid SurrogateKey { get; set; }
// Standard properties.
public string Property1 { get; set; }
public string Property2 { get; set; }
// Collection of EAV values associated with this entity/table.
[ForeignKey("ParentSurrogateKey")]
public virtual IList<EntityAttributeValue> EntityAttributeValues { get; set; }
}
public class Entity2
{
// Key. EntityAttributeBalue.ParentSurrogateKey maps to this.
[Key]
public Guid SurrogateKey { get; set; }
// Standard properties.
public string OtherProperty1 { get; set; }
public string OtherProperty2 { get; set; }
// Collection of EAV values associated with this entity/table.
[ForeignKey("ParentSurrogateKey")]
public virtual IList<EntityAttributeValue> EntityAttributeValues { get; set; }
}
My problem is that both Entity1 and Entity2 have EntityAttributeValue objects associated with them. Code first migrations tries to create a foreign key from EntityAttributeValue back to Entity1 and another one back to Entity2 on ParentSurrogateKey. The surrogate key for any single given EntityAttributeValue is only associated with either one Entity1 or one Entity2 (or, expanding out, one EntityN...), not both/all.
I have a many to many relationship here, but one side not only maps to multiple rows, but multiple entities/tables over a shared GUID column.
How should I be approaching this? Should I just remove the EntityAttributeValue foreign keys back to Entity1 and Entity2 from the automatic migration (which would be a long term pain)? Should I be manually retrieving the list of EntityAttributeValues for a given EAV entity instead of relying on EF to do it for me?
Well, the answer turned out to be obvious and simple. I needed to define a many-to-many relationship with FluentAPI. In OnModelCreating, I just added:
modelBuilder.Entity<Entity1>()
.HasMany(m => m.EntityAttributeValues)
.WithMany();
modelBuilder.Entity<Entity2>()
.HasMany(m => m.EntityAttributeValues)
.WithMany();
I thought I had tried this, but I guess I hadn't. Because the many-to-many relationship creates an intermediate table for each entity and the foreign keys are on that intermediate table (and there is only a row in the intermediate table when a given EntityAttributeValue applies to a given Entity), no foreign key issues.

Can't define two relationships between the same two entities

I can't get EF 6 Code First to map the following two relationships the way I want them.
There are two entities: Template and TemplateVersion.
Every TemplateVersion has exactly one ParentTemplate.
A Template has a collection of TemplateVersions.
This was the first, simple, 1:many relationship, with navigation properties on both sides.
Now for the second:
From all TemplateVersions associated to a Template, only one (e.g. the "newest") is the CurrentTemplateVersion for that Template.
So: Template has a navigation property CurrentVersion, and an associated property CurrentVersionId.
There is no corresponding navigation property on the TemplateVersion side.
So, I would say, this second Template : TemplateVersion relation is 0..1 : 1.
Here are the models:
public class Template
{
public int Id { get; set; }
[...]
public virtual int CurrentVersionId { get; set; }
public virtual TemplateVersion CurrentVersion { get; set; }
public virtual ICollection<TemplateVersion> Versions { get; set; }
}
public class TemplateVersion
{
public int Id { get; set; }
[...]
public virtual int ParentTemplateId { get; set; }
public virtual Template ParentTemplate { get; set; }
}
I like to keep my model classes free from DB specifics, so I defined the relationships in the context:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Template>()
.HasMany(template => template.Versions)
.WithRequired(version => version.ParentTemplate)
;
modelBuilder.Entity<Template>()
.HasRequired(template => template.CurrentVersion)
.WithOptional()
;
}
The problem is, the 2nd relation doesn't work as expected.
Using EF Power Tools plugin, I reverse-engineer the model diagram. Here's what I get:
1st relation, 1:many (ok)
2nd relation, 0..1:1
Notice that CurrentVersionId property is not part of the relation, and Template.Id is !
The generated DB tables mirror exactly this: CurrentVersionId is not part of any foreign key, and Id on the Template table incorrectly is defined as a foreign key to Id on the TemplateVersion table.
What am I missing ?
PS. Even if I remove the 1st relationship completely, the 2nd one is the same.
In a one-to-one relationship, EF requires the PK of the dependent end also has to be the FK of the relationship:
public class Foo
{
public int Id{get;set;}
//...
}
public class Boo
{
[Key,ForeignKey("Foo")]
public int FooId{get;set;}
public virtual Foo Foo{get;set;}
//...
}
If you need that TemplateVersion has its own Id, then, to resolve your issue you could configure that relationship this way:
modelBuilder.Entity<Template>()
.HasRequired(template => template.CurrentVersion)
.WithMany().HasForeignKey(t=>t.CurrentVersionId);

Foreign key as primary key in EF Code First?

I need to map a relationship between 3 domain models in my domain, where as one of the domain models is the aggregate root of the relationship model.
public class Entity1 {
public int Id { get; set; }
}
public class Entity2 {
public int Id { get; set; }
}
public class SuperEntity {
public int Id { get; set; }
// bounded context for relationship classes
}
The relationship entity should look like this
public class Relationship {
public int RelationshipId { get; set; }
public Entity1 Entity1 { get; set; }
public Entity2 Entity2 { get; set; }
}
Following this, the super entity should simply look like this:
public class SuperEntity {
public int Id { get; set; }
public ICollection<Relationship> Relationships { get; set; }
}
Now, one possibility to map this is to make the relationship a unique entity with it's own key and both entities inside the relationship unique indexes. But then the key only serves "for key purposes" without any meaningful value. Desireable would be a relationship table like this:
Table_Relationships
[ SuperEntity_Id // Foreign-key to SuperEntity
PrimaryKey [ Entity1_Id // Foreign-key to Entity1
[ Entity2_Id // Foreign-key to Entity2
Meaning that the primary key of Table_Relationships would be SuperEntity_Id+Entity1_Id+Entity2_Id.
Is it possible to map this in EF Code First?
Why not use DataAnnotations.KeyAttribute (http://msdn.microsoft.com/library/system.componentmodel.dataannotations.keyattribute%28v=vs.110%29.aspx)? It's clear way to define complex primary key in domain model classes.

Categories