I've got the following domain models (pseudo):
public class Camera {
public int Id { get; set; }
}
public class Display {
public int Id { get; set; }
}
public class SetupGroup {
public int Id { get; set; }
public virtual ICollection<CameraDisplayMap> Mappings { get; set; }
}
public class CameraDisplayMap {
public int Id { get; set; }
public Camera Camera { get; set; }
public Display Display { get; set; }
}
which should get mapped the following way:
[Cameras]
Id (primary key)
[Displays]
Id (primary key)
[SetupGroup]
Id (primary key)
[CameraDisplayMap]
Id (foreign key to [SetupGroup]
Camera (foreign key to [Cameras])
Display (foreign key to [Display])
I am aware the data model is not ideal, but it's a requirement in order to support one of our legacy applications which handled most mapping etc. with application logic.
Currently, I'm unable to configure this mapping with the given relationship instructions from EF Code First Fluent Configuration API, or at least I'm not sure how to do it. I tried mapping beginning from SetupGroup using WithMany, but here I can't declare that Camera and Display should be mapped on the CameraDisplayMap. Starting from CameraDisplayMap, I'm unable to declare the Id as being a foreign key to SetupGroup. Am I missing something?
CameraDisplayMap Class should be like following.
public class CameraDisplayMap {
public int Id { get; set; } //primary key
public int? SetupGroupId { get; set; } //foreign key to [SetupGroup]
public int? CameraId { get; set; } //foreign key to [Camera]
public int? DisplayId Displays { get; set; } //foreign key to [Display]
public virtual SetupGroup SetupGroups { get; set; }
public virtual Camera Cameras { get; set; }
public virtual Display Displays { get; set; }
}
Related
So I try to create some ASP.NET project with EF Core.
I want to set propert of one entity as primary key and foreign key to another entity. The relationship is 0..1 - 1. I use DataAnnotations:
public class OfficeAssignment
{
[Key, ForeignKey("InstructorID")]
public int InstructorID { get; set; }
public Instructor Instructor { get; set; }
public string Location { get; set; }
}
But I keep getting column InstructorID as PK and InstructorID1 as FK... Any ideas, why EF behaves like that and how can I achieve my goal?
You should follow convention over configuration as much as you can. An OfficeAssignment entity should have an OfficeAssignmentId PK, like this:
public class OfficeAssignment
{
public int OfficeAssignmentId { get; set; }
//Notice that Id does not have an uppercase D
public int InstructorId { get; set; }
public string Location { get; set; }
public Instructor Instructor { get; set; }
}
However, if you don't want to follow normal conventions, the name of the property that goes in the ForeignKey attribute is the opposite of where it's declared:
public class OfficeAssignment
{
[Key, ForeignKey("Instructor")]
public int InstructorId { get; set; }
public string Location { get; set; }
public Instructor Instructor { get; set; }
}
And, if you want to keep it compile-time safe:
public class OfficeAssignment
{
[Key, ForeignKey(nameof(Instructor))]
public int InstructorId { get; set; }
public string Location { get; set; }
public Instructor Instructor { get; set; }
}
It's enough to set primary key attribute([Key]) in the OfficeAssignment class and in Instructor class we need to set such attribute:
[InverseProperty("Instructor")]
on collection of CourseAssignments. That will work as desired.
I am trying to create a composite key using two fields when using code first to existing fields in a table in a db;
[Key,Column("driverId", Order=0)]
[JsonProperty(PropertyName="driverid")]
public override int ID { get; set; }
[Key,Column("type", Order=1)]
[JsonProperty(PropertyName="typeid")]
public int Type { get; set; }
Now when I try to run a new migration i get the following error;
The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical.
DriversToVehicle_Driver_Target_DriversToVehicle_Driver_Source: : The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical.
The DriversToVehicle table is as follows;
public partial class DriversToVehicle
{
[Column("id"), Key]
public int ID { get; set; }
[Column("driverid")]
public int DriverID { get; set; }
[ForeignKey("DriverID")]
public Driver Driver { get; set; }
[Column("vehicleid")]
public int VehicleID { get; set; }
[ForeignKey("VehicleID")]
public Vehicle Vehicle { get; set; }
}
Extending this question, originally a single key on the ID, i.e.
[Column("driverId")]
[JsonProperty(PropertyName="driverid")]
public override int ID { get; set; }
Now moving forward, how will this effect the other entities linking to it (by this i mean code first in the classes)? will ef automatically sort this out? or do I now need to have both keys in other entities when linking to this class?
e.g. as before I would have had
public virtual Driver myDriver;
Obviously now instead of linking on the ID alone it needs to be linked with the Type as well.
Thanks in advance.
EDIT FOR ANSWER
Ok, I extracted the Type out to a seperate class. The main issue is now How do i mark the foreign key as also being a composite key?
I have the following classes
public partial class DriverType
{
[Column("Id")]
[JsonProperty(PropertyName = "drivertypeid")]
public override int ID { get; set; }
[JsonProperty(PropertyName = "drivertype")]
public string Name { get; set; }
}
Then in the Driver I have the following (reduced for brevity);
public partial class Driver : AuditableEntity<int>
{
[Key,Column("driverId", Order=0)]
[JsonProperty(PropertyName="driverid")]
public override int ID { get; set; }
[Key,Column("type", Order=1)]
[ForeignKey("DriverType")]
[JsonProperty(PropertyName="drivertypeid")]
public int DriverTypeId { get; set; }
public DriverType DriverType { get; set; }
How do I then add it to the DriverToVehicle class please? So far I have
public partial class DriversToVehicle
{
[Column("id"), Key]
public int ID { get; set; }
[Column("driverid", Order=0), ForeignKey("Driver")]
public int DriverID { get; set; }
public Driver Driver { get; set; }
[Column("type", Order = 1), ForeignKey("Driver")]
public int DriverTypeId { get; set; }
[ForeignKey("DriverTypeId")]
public DriverType DriverType { get; set; }
}
This doesnt look right to me though?
Since your Drivers table's Primary Key is now (DriverId, Type), you can no longer reference your drivers by DriverId alone - you must reference them by both DriverId and Type. Therefore, your DriversToVehicle table needs to look something like this:
public partial class DriversToVehicle
{
[Column("id"), Key]
public int ID { get; set; }
[Column("driverid")]
public int DriverID { get; set; }
[ForeignKey("DriverID")]
public Driver Driver { get; set; }
[Column("DriverType")]
public int DriverType { get; set; }
[ForeignKey("type")]
public int DriverType { get; set; }
[Column("vehicleid")]
public int VehicleID { get; set; }
[ForeignKey("VehicleID")]
public Vehicle Vehicle { get; set; }
}
However, as #hopeless states above, you may not need to model this join table if you correctly model your Driver and Vehicle types correctly.
HTH.
I'm trying to create a linked table that will allow me to have a many to many relationship between my product and accessory tables.
My classes are like this:
public class Product {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Accessory> Accessories { get; set; }
}
public class Accessory {
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Machine> Machine { get; set; }
}
public class Type {
public int Id { get; set; }
public string Name { get; set; }
}
The same accessory can be on a product more than once if it is a different type, which will be determined in the link table. Something like this:
public class ProductAccessoryLink {
public int productId {get; set;}
public int accessoryId {get; set;}
public int typeId {get; set}
public int sort {get; set;}
public string notes {get; set}
}
Is this the right approach.
EDIT
This is the error I'm getting when I run update-database:
Introducing FOREIGN KEY constraint
'FK_dbo.ProductAccessoryLinks_dbo.Types_TypeId' on table
'ProductAccessoryLinks' may cause cycles or multiple cascade paths.
Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other
FOREIGN KEY constraints. Could not create constraint. See previous
errors.
This is the sql causing the error: ALTER TABLE
[dbo].[ProductAccessoryLinks] ADD CONSTRAINT
[FK_dbo.ProductAccessoryLinks_dbo.Types_TypeId] FOREIGN KEY ([TypeId])
REFERENCES [dbo].[Types] ([Id]) ON DELETE CASCADE
In your case you need to map explicitly the junction table. Your model would be like this:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ProductAccessoryLink> ProductAccessoryLinks { get; set; }
}
public class Accessory
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ProductAccessoryLink> ProductAccessoryLinks { get; set; }
}
public class Type
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ProductAccessoryLink
{
public int ProductId { get; set; }
public int AccessoryId { get; set; }
public int TypeId { get; set; }
public int sort { get; set; }
public string notes { get; set; }
public virtual Type Type { get; set; }
public virtual Product Product { get; set; }
public virtual Accessory Accessory { get; set; }
}
And you could configure the relationships overriding the OnModelCreating method on your context this way:
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ProductAccessoryLink>().HasKey(i => new { i.ProductId, i.AccesoryId, i.TypeId});
modelBuilder.Entity<ProductAccessoryLink>()
.HasRequired(i => i.Product)
.WithMany(k => k.ProductAccessoryLinks)
.HasForeignKey(i=>i.ProductId);
modelBuilder.Entity<ProductAccessoryLink>()
.HasRequired(i => i.Accesory)
.WithMany(k => k.ProductAccessoryLinks)
.HasForeignKey(i=>i.AccesoryId);
modelBuilder.Entity<ProductAccessoryLink>()
.HasRequired(i => i.Type)
.WithMany()
.HasForeignKey(i=>i.TypeId);
}
EF lets you configure directly many-to-many relationships of the way you were attempting to. Thereby EF is responsible for build a join table in the database with the appropriate keys of the tables it’s joining. (The keys are both primary keys of the join table and foreign keys pointing to the joined tables). That lets you to get your data across the join table without you having to be aware of its presence. But when you want to personalize that table (adding, for example, some additional properties), you need to map it explicitly as I show above.
Update
That exception is caused when you have multiple paths of cascade deletes that could end trying to delete the same row in the Types table. To resolve that problem I recommend you check my answer in this post
So i am trying to create a table with a foreign key, but it always says that it cannot find the foreign key. heres the code:
public class Tecnologies
{
[Key]
public int TecId { get; set; }
[Required]
public String Name { get; set; }
}
this one works, then i try to create this one:
public class UserTecnologies
{
[Key]
public int UserTecId { get; set; }
[ForeignKey("Id")]
public UserProfile User { get; set; }
[ForeignKey("TecId")]
public virtual Tecnologies Tecnology { get; set; }
[Required]
public int Rating { get; set; }
}
and it gives me the error :
The ForeignKeyAttribute on property 'Tecnology' on type 'ESW_CloddOffice.Models.UserTecnologies' is not valid. The foreign key name 'TecId' was not found on the dependent type 'ESW_CloddOffice.Models.UserTecnologies'. The Name value should be a comma separated list of foreign key property names.
The names are correct, what am i missing ?
Okay, i found what i was doing wrong. Heres the correct code:
public class UserTecnologies
{
[Key]
public int UserTecId { get; set; }
[ForeignKey("UserProfile")]
public virtual int UserProfileId { get; set; }
[ForeignKey("Tecnology")]
public virtual int TecnologyId { get; set; }
public virtual Tecnologies Tecnology { get; set; }
public virtual UserProfile UserProfile { get; set; }
[Required]
public int Rating { get; set; }
}
Was creating the foreign key the wrong way .
The ForeignKey attribute requires that an actual property on the entity match the name you pass in. It doesn't just tell EF what to call the key at the database level.
You either need to actually add a TecId property:
public int TecId { get; set; }
Or use fluent configuration, instead:
modelBuilder.Entity<UserTechnologies>()
.HasRequired(c => c.Technology)
.WithMany()
.Map(m => m.MapKey("TecId"));
Shortly, I want to create composite keys on my table remaining with the primary key in order to improve sql server search performance. The performance issue occurs on 200k data table whenever I search an entity without primary key (i.e a string of GUID). Assume that I have 3 classes
public class Device{
public int ID { get; set; }
public string UDID { get; set; }
public string ApplicationKey { get; set; }
public string PlatformKey { get; set; }
public ICollection<NotificationMessageDevice> DeviceMessages { get; set; }
}
public class NotificationMessageDevice {
[Column(Order = 0), Key, ForeignKey("NotificationMessage")]
public int NotificationMessage_ID { get; set; }
[Column(Order = 1), Key, ForeignKey("Device")]
public int Device_ID { get; set; }
public virtual Device Device { get; set; }
public virtual NotificationMessage NotificationMessage { get; set; }
}
public class NotificationMessage {
public int ID { get; set; }
public string Text { get; set; }
public DateTime CreateDate { get; set; }
}
modelBuilder.Entity<Device>().HasKey(t => new { t.ID, t.ApplicationKey, t.PlatformKey, t.UDID });
What the problem is that whenever I want to make ID , UDID , ApplicationKey and PlatformKey define as a Composite Key with modelBuilder it gives the following error.
NotificationMessageDevice_Device_Target_NotificationMessageDevice_Device_Source:
: The number of properties in the Dependent and Principal Roles in a
relationship constraint must be identical
I think the problem is because the navigation property on NotificationMessageDevice is not able to recognize what the primary key is on Device table. How can I resolve this problem? In addition to this I will be glad if you share your experiences improving the search performance on Entity framework. Usually the performance issue occurs on whenever I use First method without primary keys.
If Device table has composite primary key, then you need same composite foreign key on your NotificationMessageDevice table. How would SQL find Device without full primary key? Also you should make these fields to be part of NotificationMessageDevice table primary key. Otherwise you can't guarantee primary key will be unique:
public class NotificationMessageDevice
{
[Column(Order = 0), Key, ForeignKey("NotificationMessage")]
public int NotificationMessage_ID { get; set; }
[Column(Order = 1), Key, ForeignKey("Device")]
public int Device_ID { get; set; }
[Column(Order = 2), Key, ForeignKey("Device")]
public string Device_UDID { get; set; }
[Column(Order = 3), Key, ForeignKey("Device")]
public string Device_ApplicationKey { get; set; }
public virtual Device Device { get; set; }
public virtual NotificationMessage NotificationMessage { get; set; }
}