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.
Related
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 have the following (abbreviated) models:
public class Item
{
public int id { get; set; }
public string itemId { get; set; }
... more properties ...
public virtual List<Detail> Details { get; set;}
}
public class Detail
{
[Key]
public int id { get; set; }
public string itemId { get; set; }
... more properties ...
// Navigation property
[ForeignKey("itemId")]
public virtual Item Item { get; set; }
}
If I use itemId as an int, it'll create the FK, however it'll link Item.ID to Detail.itemId - I'd like it to link Item.itemId to Detail.itemId
I'm sure it's something I'm missing in the decorations however it seems that EF want to always use the default ID.
The reason I'm looking to do it this way is because the source data is linked via a string ID, which I can convert to a int but the limitation remains, that each table I'd prefer to have it's own PK until I can make sure the source data is robust enough.
The foreign key in the dependent needs to link (edit - usually links) to the primary key of the principal. If you want these to be strings then all you should need to do is follow the naming conventions for keys and foreign keys::
public class Item
{
//Code First infers that a property is a primary key if a property
//on a class is named “ID” (not case sensitive),
//or the class name followed by "ID"
//so you could use "Id" for the name of the primary key
public string ItemId { get; set; }
//... more properties ...
public virtual List<Detail> Details { get; set;}
}
public class Detail
{
//Let's use DetailId as the key here because that is the convention
//we've used in the "Item" class
public int DetailId { get; set; }
/*Any property with the same data type as the principal primary key
property and with a name that follows one of the following formats
represents a foreign key for the relationship:
<navigation property name><principal primary key property name> (i.e.ItemItemId),
<principal class name><primary key property name>(i.e. ItemItemId),
or <principal primary key property name>(i.e. ItemId).
If multiple matches are found then precedence is given in the order listed above.*/
public string ItemId { get; set; }
//... more properties ...
public virtual Item Item { get; set; }
}
No need for attributes because all the names follow the naming convention for keys and foreign keys.
Now, if you want to add a field to the Item class named Id that is not the primary key (?!!) then you will need to tell Entity Framework that ItemId is the primary key - you can do that with the Key attribute:
public class Item
{
[Key]
public string ItemId { get; set; }
/*Because it is not the primary key, if you want it to be an Identity
field, you may need to add the attribute*/
[DatabaseGenerated(DatabaseGeneratedoption.Identity)]
public int Id {get; set; }
}
EDIT
Made after your comment, it's probably as unconventional as it comes to have foreign keys that don't refer to the primary key, but you are not tied to convention. You override conventions using data attributes or the fluent api.
In this case, you can probably force EF to do it by using the InverseProperty on the navigation property of the dependent (I say "probably" because I haven't tried this so don't actually know if EF will protest):
public class Item
{
public int Id {get; set; }
public string ItemId { get; set; }
public virtual List<Detail> Details { get; set;}
}
public class Detail
{
public int DetailId { get; set; }
public string ItemId { get; set; }
[InverseProperty("ItemId")] //NB EF will look in the principal for this
//i.e. the Item class
public virtual Item Item { get; set; }
}
Reference:
Code first conventions
Relationships with Data Attributes
Relationships with the FluentAPI
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.
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.
I am trying to do code first with annotations (for the first time) on an MVC project.
I have created the following POCOs.
[Table("Customers")]
public partial class Customer
{
public int Id { get; set; }
[Required]
[DisplayName("First Name")]
public string FirstName { get; set; }
[DisplayName("Last Name")]
[Required]
public string LastName { get; set; }
//other properties...
}
[Table("Vehicles")]
public partial class Vehicle
{
[Required]
public int Id { get; set; }
[Required]
public int CustomerId { get; set; }
[Required]
public string Make { get; set; }
[Required]
public string Model { get; set; }
[Required]
public string Year { get; set; }
//other fields
[ForeignKey("CustomerId")]
public virtual Customer Customer { get; set; }
}
[Table("CustomerAppointments")]
public partial class CustomerAppointment
{
[Key,Column(Order=0)]
public int CustomerId { get; set; }
[Key,Column(Order=1)]
public int VehicleId { get; set; }
public DateTime? AppointmentDate { get; set; }
public DateTime? AppointmentTime { get; set; }
public string AvailableDays { get; set; }
//other fields
[ForeignKey("CustomerId")]
public virtual Customer Customer { get; set; }
[ForeignKey("VehicleId")]
public virtual Vehicle Vehicle { get; set; }
}
I think my intent here is fairly obvious. I have customers. Those customers have vehicles. I want to create a table CustomerAppointments where a customer and one of the customers vehicles is scheduled for a service.
For the record, this is not the whole model and has been simplified for the purposes of the question.
I am using MvcScaffolding to build out the EF items and the views.
Everything compiles but when I try to navigate to the Customers page (actually a class not mentioned that references customers) I am getting the following error...
Introducing FOREIGN KEY constraint 'FK_dbo.CustomerAppointments_dbo.Vehicles_VehicleId' on table 'CustomerAppointments' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
I have tried different annotations and even tried to use the fluent API with something like this...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<CustomerAppointment>()
.HasRequired(ca => ca.Customer)
.WithRequiredPrincipal()
.WillCascadeOnDelete(false);
modelBuilder.Entity<CustomerAppointment>()
.HasRequired(ca => ca.Vehicle)
.WithRequiredPrincipal()
.WillCascadeOnDelete(false);
}
But I cannot get it to work. I have read every sample I can find on google and SO but to no avail.
PS...if this can work with Annotations only that would be my preference.
Your model has two cascading delete paths from Customer to CustomerAppointment when a Customer is deleted:
Customer -> Vehicle -> CustomerAppointment
Customer -> CustomerAppointment
That's not allowed in SQL Server and causes the exception. You need to disable cascading delete for at least one of those three subpaths which is only possible with Fluent API. For example the Customer -> Vehicle path:
modelBuilder.Entity<Vehicle>()
.HasRequired(v => v.Customer)
.WithMany()
.HasForeignKey(v => v.CustomerId)
.WillCascadeOnDelete(false);
You could also make CustomerId nullable to have an optional relationship in which case EF will disable cascading delete by default. But changing a required to an optional relationship expresses a change in business rules which I wouldn't do just to avoid Fluent API.
BTW: Is it really correct that CustomerAppointment should have a composite primary key? It would mean that a given customer with a given vehicle could only have one service appointment. Couldn't there be many appointments for the same customer/vehicle combination at different appointment dates? If yes, you should rather have a separate key for CustomerAppointment and CustomerId and VehicleId would be just foreign keys without being part of the primary key.
It seems like you are better off using the database-first approach and then generating the model using ado enity data model.
By convention, cascade deletes are handled by the introduction of the actual foreign key into your model. If you use a non-nullable foreign key, it will require delete. Use a nullable foreign key to turn it off.
Change your class to the following by making the foreign keys nullable:
[Table("CustomerAppointments")]
public partial class CustomerAppointment
{
[Key,Column(Order=0)]
public int? CustomerId { get; set; }
[Key,Column(Order=1)]
public int? VehicleId { get; set; }
public DateTime? AppointmentDate { get; set; }
public DateTime? AppointmentTime { get; set; }
public string AvailableDays { get; set; }
//other fields
[ForeignKey("CustomerId")]
public virtual Customer Customer { get; set; }
[ForeignKey("VehicleId")]
public virtual Vehicle Vehicle { get; set; }
}
Remember to also remove the fluent mapping.
From http://msdn.microsoft.com/en-US/data/jj679962
If a foreign key on the dependent entity is not nullable, then Code
First sets cascade delete on the relationship. If a foreign key on the
dependent entity is nullable, Code First does not set cascade delete
on the relationship, and when the principal is deleted the foreign key
will be set to null.