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.
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'm trying to add some entities using EntityFramework.
I need the same model as in image
I created two classes:
public class PriceOfDish
{
[Key]
public virtual List<Dish> Dish_ID { get; set; }
[Key]
public DateTime DateTime { get; set; }
public decimal Price { get; set; }
}
public class Dish
{
[Key]
public int Dish_ID { get; set; }
public string DishName { get; set; }
public string Description { get; set; }
public virtual FoodCategory FoodCategory_ID { get; set; }
public virtual Feature Feature_ID { get; set; }
public virtual ICollection<OrderedDishes> Orders { get; set; }
}
Using FluentAPI trying to set primary keys:
builder.Entity<PriceOfDish>()
.HasKey(t => new {t.Dish_ID, t.DateTime});
During updating DB i get error message: "The property 'Dish_ID' cannot be used as a key property on the entity 'testFOef.PriceOfDish' because the property type is not a valid key type. Only scalar types, string and byte[] are supported key types.".
But why? Can you explain this to me? Thanks for any help
Like the error say you are using a List<Dish> as a type for your primary key. You must use an scalar type (value type) as usual or byte[].
For you, the solution is to create a proprerty Dish_ID with int type.
public class PriceOfDish
{
[Key]
public int Dish_ID { get; set; }
[Key]
public DateTime DateTime { get; set; }
[ForeignKey("Dish_Id")]
public virtual Dish Dish { get; set; }
public decimal Price { get; set; }
}
you're trying to set a List<Dish> Dish_ID as a key of a table.
But this is not supported. It doesn't make much sense either. list of Dishes that will have a single Price? I think you want to put int Dish_ID there
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; }
}
I'm building an MVC5 project using Code First migrations with EF6 in Visual Studio 2013. Everything has been working as expected up to now: multiple migrations adding multiple tables to my db. After add a new class that should map to a new database table, add-migration won't pick up the new class and the new migration file is generated with no code in the Up() and Down() methods. The class in question is as simple as it can be:
[Table("SurveyItemOption")]
public class SurveyItemOption
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Option { get; set; }
public virtual SurveyItem SurveyItem { get; set; }
}
As you can see, I've added the DatabaseGenerated attribute myself to see if that makes any difference, but it still doesn't pick up the new class and create the new table. SurveyITem is the parent class and its db table is generated with Code First, as is the Survey class which is the parent of SurveyItem. Below is the other code that is successfully generated.
public enum ListType
{
None = 0,
Circle = 1,
Square = 2,
LowerAlpha = 3,
LowerLatin = 4,
LowerRoman = 5,
UpperAlpha = 6,
UpperLatin = 7,
UpperRoman = 8
}
[Table("Survey")]
public class Survey
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Title { get; set; }
public bool IsActive { get; set; }
public string Owner_UserId { get; set; }
public int Module_Id { get; set; }
public virtual IEnumerable<SurveyItem> Items { get; set; }
}
[Table("SurveyItem")]
public class SurveyItem
{
public SurveyItem() {
this.OptionListType = ListType.None;
}
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Item { get; set; }
public int Survey_Id { get; set; }
[EnumDataType(typeof(ListType))]
public ListType OptionListType { get; set; }
public virtual int OptionListTypeId
{
get
{
return (int)this.OptionListType;
}
set
{
this.OptionListType = (ListType)value;
}
}
}
[Table("SurveyAnswer")]
public class SurveyAnswer
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string UserId { get; set; }
public string Answer { get; set; }
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Computed)]
public DateTime Created { get; set; }
public virtual SurveyItem SurveyItem { get; set; }
}
I've gone as far as creating a new project and imported all my Models, Views and Controllers (along with various Content files) from this project, built the project, and enabled migrations. The SurveyItemOption class is still ignored in the initial migration code.
Please help! Any ideas on how to fix this would be much appreciated!
I'm sure there's a good technical explanation for why this table was not getting generated by Code First, but I realized something was lacking in the relationship between SurveyItem, SurveyItemOption and SurveyAnswer (which stores the answers for a user). Initially, I was not going to include item options so I only had a relationship between SurveyItem and SurveyAnswer. SurveyAnswer had a foreign key to SurveyItem so my guess is EF didn't like that I was creating the same relationship on a different table. I also realized that SurveyAnswer needed a foreign key to SurveyItemOption in order to store a SurveyItemOption as the answer. Adding this relationship solved my problem.
[Table("SurveyAnswer")]
public class SurveyAnswer
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string UserId { get; set; }
public string Answer { get; set; }
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Computed)]
public DateTime Created { get; set; }
public virtual SurveyItem SurveyItem { get; set; }
public virtual SurveyItemOption SurveyItemOption { get; set; }
}
UPDATE:
It seems that another requirement is to make sure you have a DbSet set up on your data context, too.
Is it possible to have a foreign key mapping based on a specific column value.
I have the following entities.
public class Controller
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual List<ControllerDevice> ActiveDevices { get; set; }
public virtual List<ControllerDevice> TamperedDevices { get; set; }
public virtual List<ControllerDevice> IgnoredDevices { get; set; }
}
public class ControllerDevice
{
public int Id { get; set; }
public DeviceStatus Status { get; set; }
public int ControllerId { get; set; }
public int NetworkDeviceId { get; set; }
public virtual Controller Controller { get; set; }
public virtual NetowkDevice NetowkDevice { get; set; }
}
public class NetowkDevice
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
public enum DeviceStatus
{
Active,
Tampered,
Ignored
}
Is it possible to have the ActiveDevices, TamperedDevices and IngoredDevices list be auto populated based on ControllerDevice DeviceStatus, or would I have to create three different tables for each list. IE ActiveControllerDevice, TamperedControllerDevices and IgnoredControllerDevices.
Please let me know if you require further explanation.
Use single devices collection:
public class Controller
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual List<ControllerDevice> Devices { get; set; }
}
...and filter it, when you need to process or display devices with specific Status value:
controller.Devices.Where(d => d.Status == DeviceStatus.Active);
Several tables for each devices status, and/or devices hierarchy (theoretically, you can solve this problem with a TPH inheritance) is a way to hell, because instead of single entity ControllerDevice with a status you'll get three entity types (ActiveControllerDevice, TamperedControllerDevice and IgnoredControllerDevice), which is not corresponding to model.
Instead of changing status, the device will change its type, and you cannot do that in simple way.
public class TestContext : DbContext
{
public TestContext()
{
Configuration.AutoDetectChangesEnabled = true;
Configuration.LazyLoadingEnabled = true;
Configuration.ProxyCreationEnabled = true;
Configuration.ValidateOnSaveEnabled = true;
}
public virtual DbSet<NetowkDevice> NetowkDevices{ get; set; }
public virtual DbSet<ControllerDevice> ControllerDevices{ get; set; }
public virtual DbSet<Controller> Controlleres{ get; set; }
}
http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/d0443029-2175-4bde-a834-4f8dbf313201/
Should I enable or disable dynamic proxies with entity framework 4.1 and MVC3?
Yes, you can do that. Enum support was introduced in Entity Framework 5, .Net Framework 4.5. In Entity Framework, an enumeration can have the following underlying types: Byte, Int16, Int32, Int64 , or SByte.
And you can filter like this:
context.ControllerDevices.Where(d => d.Status == DeviceStatus.Active);
More here: http://msdn.microsoft.com/en-us/data/hh859576.aspx