RemarkLima
RemarkLima

Reputation: 12037

Entity Framework 6 Foreign Key using strings and not to source model ID

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.

Upvotes: 1

Views: 9367

Answers (1)

Colin
Colin

Reputation: 22595

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

Upvotes: 3

Related Questions