Mapping a foreign key with a custom column name - c#

I'm using Entity Framework 4.3 code-first with Oracle. I'm getting the following error:
System.InvalidOperationException : The ForeignKeyAttribute on property 'WidgetSequence' on type 'WidgetDistributor.WidgetEntity' is not valid. The foreign key name 'WIDGETSEQUENCE_ID' was not found on the dependent type 'WidgetDistributor.WidgetEntity'. The Name value should be a comma separated list of foreign key property names.
My entities are like this:
[Table("WIDGETENTITIES")]
public class WidgetEntity {
[Column("WIDGETENTITY_ID")]
public int Id { get; set; }
[ForeignKey("WIDGETSEQUENCE_ID")]
public WidgetSequence Sequence { get; set; }
// and other properties that map correctly
}
[Table("WIDGETSEQUENCES")]
public class WidgetSequence {
[Column("WIDGETSEQUENCE_ID")]
public int Id { get; set; }
[Column("NUMBER")]
public int Number { get; set; }
}
My code seems correct. What have I done wrong, here?

If you don't want to use fluent syntax, there are three other ways of implementing the reference using data annotations (Personally I prefer data annotations as they seem easier to read and are written just above the property they are affecting):
1.1)
Use ForeignKey (with an associated property) - version 1
[Table("WIDGETENTITIES")]
public class WidgetEntity {
[Column("WIDGETENTITY_ID")]
public int Id { get; set; }
[Column("WIDGETSEQUENCE_ID")]
public int WidgetSequenceId { get; set; }
[ForeignKey("WidgetSequenceId")] //Has to be a property name, not table column name
public WidgetSequence Sequence { get; set; }
// and other properties that map correctly
}
[Table("WIDGETSEQUENCES")]
public class WidgetSequence {
[Column("WIDGETSEQUENCE_ID")]
public int Id { get; set; }
[Column("NUMBER")]
public int Number { get; set; }
}
1.2)
Use ForeignKey (with an associated property) - version 2
[Table("WIDGETENTITIES")]
public class WidgetEntity {
[Column("WIDGETENTITY_ID")]
public int Id { get; set; }
[ForeignKey("Sequence")] //Has to be a property name, not table column name
[Column("WIDGETSEQUENCE_ID")]
public int WidgetSequenceId { get; set; }
public WidgetSequence Sequence { get; set; }
// and other properties that map correctly
}
[Table("WIDGETSEQUENCES")]
public class WidgetSequence {
[Column("WIDGETSEQUENCE_ID")]
public int Id { get; set; }
[Column("NUMBER")]
public int Number { get; set; }
}
2)
You can also use the InversePropertyAttribute.
[Table("WIDGETENTITIES")]
public class WidgetEntity {
[Column("WIDGETENTITY_ID")]
public int Id { get; set; }
[InverseProperty("WidgetEntities")]
public WidgetSequence Sequence { get; set; }
// and other properties that map correctly
}
[Table("WIDGETSEQUENCES")]
public class WidgetSequence {
[Column("WIDGETSEQUENCE_ID")]
public int Id { get; set; }
[Column("NUMBER")]
public int Number { get; set; }
public virtual List<WidgetEntity> WidgetEntities { get; set; }
}

ForeignKey attibute expects a property name in your class as the argument but you given the column name. Use fluent mappings.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<WidgetEntity>()
.HasRequired(w => w.Sequence)
.WithMany()
.Map(m => m.MapKey("WIDGETSEQUENCE_ID"));
}

There is a table called Users and it has a primary key called UserID.
There is another table called Directory, and it has a column called UserID which is defined as a foreign key to the Users table.
I'm able to use the ForeignKey annotation to map the foreign key like this:
[ForeignKey("xyzzy")]
public int? UserID { get; set; } // This is a column in the table
public virtual User xyzzy { get; set; } // This is my instance of User

Related

Automapper, Entity Framework Core and multiple nested collections

My database has two tables - RuleGroups and Rules. My Entity Framework classes are the following:
public class RuleGroup
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<Rule> Rules { get; set; }
}
public class Rule
{
[Key]
public Guid Id { get; set; }
public Guid RuleGroupId { get; set; }
public string Name { get; set; }
public ICollection<Condition> Conditions { get; set; }
[ForeignKey("RuleGroupId")]
public virtual RuleGroup RuleGroup { get; set; }
}
[NotMapped]
public class Condition
{
public Guid Id { get; set; }
public string Name { get; set; }
}
Class Condition is not mapped because it is being serialized and stored as JSON in Rule Table (using this example)
My DTOS are the following:
public class UpdateRuleGroupDto
{
public string Name { get; set; }
public ICollection<UpdateRuleDto> Rules { get; set; }
}
public class UpdateRuleDto
{
public string Name { get; set; }
public ICollection<UpdateConditionDto> Conditions { get; set; }
}
public class UpdateConditionDto
{
public string Name { get; set; }
}
In my Startup.cs I initialize Automapper :
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.CreateMap<UpdateRuleGroupDto, RuleGroup>();
cfg.CreateMap<UpdateRuleDto, Rule>();
cfg.CreateMap<UpdateConditionDto, Condition>();
}
I have an API controller endpoint that accepts JSON PATCH document to make changes to data stored in database.
public IActionResult Patch(Guid ruleGroupId, [FromBody]JsonPatchDocument<UpdateRuleGroupDto> body)
{
RuleGroup ruleGroupFromRepo = _deviceRules.GetRuleGroup(ruleGroupId);
UpdateRuleGroupDto ruleGroupToPatch = Mapper.Map<UpdateRuleGroupDto>(ruleGroupFromRepo);
// Patching logic here
Mapper.Map(ruleGroupToPatch, ruleGroupFromRepo);
context.SaveChanges();
return NoContent();
}
The problem:
When changes are made/saved, Rules in Rule table change their/get new GUID.
Example, say we have this data in 2 Tables.
RuleGroup Table
[Id][Name]
[ddad5cac-e5a1-4db7-8167-66a6de3b8a0c][Test]
Rule Table
[Id][RuleGroupId][Name][Condition]
[17c38ee8-4158-4ecc-b893-97786fa76e13][ddad5cac-e5a1-4db7-8167-66a6de3b8a0c][Test][[{"Name":"Test"}]]
If I change field [Name] to a new value, Rules Table will look like this.
Rule Table
[Id][RuleGroupId][Name][Condition]
[ba106de8-bcbc-4170-ba56-80fe619cd757][ddad5cac-e5a1-4db7-8167-66a6de3b8a0c][Test2][[{"Name":"Test"}]]
Note that [Id] field has now a new GUID.
EDIT
#Gert Arnold made me realize that I'm not attaching entities.
I ran the following code:
foreach (var item in ruleGroupFromRepo.rules)
{
var x = _context.Entry(item).State;
}
and all the states were Added and not modified. Now I just have to figure out how to do it properly.

Composite Key and using Multiple Column Attributes in Entity Framework 6

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.

Entity framework code first many to many with extra key

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

Foreign key invalid with Entity Framework

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"));

Problems with mapping relationship in EF

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; }
}

Categories

Resources