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.
Related
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; }
}
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
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.
I'm using entity framework to manage my sql-server-ce database. i want my table's primary key to be consisted of several foreign keys to other tables. i expected something like this to work:
class Bill{
[Key]
public virtual Customer Customer { get; set; }
[Key]
public virtual Era Era { get; set; }
[Key]
public virtual CompanyCode CompanyCode { get; set; }
public long? Amount { get; set; }
}
but it results in the following database migration error:
BillPrinter.Bill: : EntityType 'Bill' has no key defined. Define the
key for this EntityType.
Bills: EntityType: EntitySet 'Bills' is based on type 'Bill' that has
no keys defined.
how can i make my table have a primary key consisted of those three foreign keys?
You can't use navigation properties as PKs. Navigation properties provide a way to navigate an association between two entity types but they don't represent by themselves the FK of the relationship. You need to declare explicitly three additional properties to represent the FKs of your relationships, like in this model:
public class Customer
{
public int Id {get;set;}
//...
}
public class Era
{
public int Id {get;set;}
//...
}
public class CompanyCode
{
public int Id {get;set;}
//...
}
public class Bill
{
[Key]
[Column(Order=1)]
[ForeignKey("Customer")]
public int CustomerId {get;set;}
[Key]
[Column(Order=2)]
[ForeignKey("Era")]
public int EraId {get;set;}
[Key]
[Column(Order=3)]
[ForeignKey("CompanyCode")]
public int CompanyCodeId {get;set;}
//...
public virtual Customer Customer { get; set; }
public virtual Era Era { get; set; }
public virtual CompanyCode CompanyCode { get; set; }
}
As you can see, when you have composite keys, Entity Framework requires you to define an order of the key properties. You can do this using the Column annotation to specify an order. Also, you need to use the ForeignKey data annotation to clarify your intention which navigation property represents the relationship it is a foreign key for.
Take the following C# code. I am attempting to create ClassC which has a composite key but also create an identity in there that can be used as a foreign key in another table ClassD. I'd rather use this foreign key instead of trying to map the composite key which seems a bit odd.
public class ClassA
{
[Key]
public int ClassAID { get; set; }
public virtual ICollection<ClassC> SomeClassCs { get; set; }
}
public class ClassB
{
[Key]
public int ClassBID { get; set; }
public virtual ICollection<ClassC> SomeClassCs { get; set; }
}
public class ClassC
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ClassCID { get; set; }
[Key, Column(Order=0), ForeignKey("SomeA")]
public int ClassAID { get; set; } //Foreign Keys combined as Primary Key
[Key, Column(Order=1), ForeignKey("SomeB")]
public int ClassBID { get; set; }
public virtual ClassA SomeA { get; set; }
public virtual ClassB SomeB { get; set; }
}
public class ClassD
{
[Key]
public int ClassDID { get; set; }
[ForeignKey("SomeC")]
public int ClassCID { get; set; }
public virtual ClassC SomeC { get; set; }
}
On creating my migration I receive the following error:
System.Data.Entity.Edm.EdmAssociationConstraint: : The number of
properties in the Dependent and Principal Roles in a relationship
constraint must be identical.
Anyone solved this, or am I approaching this incorrectly?
"I'd rather use this foreign key instead of trying to map the composite
key which seems a bit odd"
You are talking about Natural vs Surrogate keys
This is what I do:
Use identity (surrogate) keys on all my models
Validate the data by
overriding ValidateEntity in the context class - prevent duplicates
in entity framework
Add unique indexes to the natural keys -
create indexes in migrations
Entity Framework does not provide data annotation attributes for identifying properties that are natural keys or should have unique indexes - but if you look at the answers in the links I've provided you will see that some people create custom attributes for that purpose
Foreign key of Dependent entity should contain all Primary keys of the Principal. You can read more about foreign key constraints on TechNet.
You have two primary keys on ClassC so, you should have two foreign keys on ClassD for this relation. Or you can make ClassCID to be single primary key, then your mapping for ClassD foreign key will work. In any case - foreign key of dependent table should be exactly same as primary key in principal table.