Entity Framework Custom Navigational Properties - c#

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

Related

Mapping 1-0..1 Relationship with Navigation Property Without FK

I've got 2 entities with a 1-0..1 relationship between them, but restrictions on what the generated DB schema can look like.
So 1 Vehicle to 0 or 1 RecVehicle entity
I need to be able to have a navigation property from Vehicle to RecVehicle, but without the DB Schema for the Vehicles table having a FK to RecVehicle. The PK of the RecVehicle table should be the Id of the Vehicle entity it relates to.
We are using EF code first
public class Vehicle
{
[Key]
public int Id { get; set; }
public virtual RecVehicle RecVehicle { get; set; } // Need to be able to use as navigation
}
public class RecVehicle
{
[Key]
public int VehicleId { get; set; }
[ForeignKey("VehicleId")]
public Vehicle Vehicle { get; set; }
}
The generated schema needs to be something like this:
Vehicles
[ Id(int, pk, not null), ...] <-- no FK column to RecVehicles
RecVehicles
[ VehicleId(int, pk, fk, not null), ...]
Originally what I had tried something like this:
public class Vehicle
{
[Key]
public int Id { get; set; }
[InverseProperty("Vehicle")]
public virtual RecVehicle RecVehicle { get; set; } // Need to be able to use as navigation
}
but this causes this exception:
Unable to determine the principal end of an association between the types 'Contract.Entities.Vehicle' and 'Contract.Entities.RecVehicle'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
I'm not sure what fluent API relationships to setup to make this work, nor the correct set of data annotations to make this work, or if it's even possible.
Reasoning
The reason there is strict limitations on the DB schema is our Data team has a migration/data import process that we can not alter
We have an existing code base that uses the navigation property in many places (2 teams, desync in schema) so changing to use a lookup in code requires many changes in the code base that we are trying to avoid.
The RecVehicle can be connected to multiple Vehicles
Can you try the following navigation property?
public virtual ICollection<RecVehicle> RecVehicle { get; set; }
instead of
public virtual RecVehicle RecVehicle { get; set; }
Due to the RecVehicle primary key this list only maximum contains one element
Ended up being able to get this relationship to work like this:
public class Vehicle
{
[Key]
public int Id { get; set; }
public virtual RecVehicle RecVehicle { get; set; }
}
public class RecVehicle
{
[Key]
public int VehicleId { get; set; }
[ForeignKey("VehicleId"), Required] //<--- Required attr fixed the principal/dependent confusion EF was having
public virtual Vehicle Vehicle { get; set; }
}

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.
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

set multiple foreign keys as primary keys in entity framework

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.

Defining a composite key and identity on the same table using EF Code First

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.

Entity Framework 0..1 to 0 relation

class First
{
[Key]
public int Id { get; set; }
}
class Second
{
[Key]
public int Id { get; set; }
public int? First_Id { get; set; }
[ForeignKey("First_Id")]
public First First { get; set; }
}
public class SecondMapping : EntityTypeConfiguration<Second>
{
public SecondMapping ()
: base()
{
this.HasOptional(s => s.First)
.With ... ???
}
}
Second may have a reference to First. But First never has a reference to Second. Is it possible to apply this mapping with Entity Framework 4.1?
EDIT:
Previously, that was my solution:
this.HasOptional(s => s.First)
.WithOptionalDependent()
.WillCascadeOnDelete(false);
Second could contain one instance of First (dependent on some kind of Usage-Attribute). First doesn't contain any instance of Second.
One-to-one relation is possible only if foreign key is also primary key of dependent entity. So the correct mapping is:
class First
{
[Key]
public int Id { get; set; }
}
class Second
{
[Key, ForeignKey("First")]
public int Id { get; set; }
public First First { get; set; }
}
The reason is that to enforce one-to-one relation in the database foreign key must be unique in the Second entity. But entity framework doesn't support unique keys - the only unique value for EF is primary key. This is limitation of EF.
There is workaround described on Morteza Manavi's blog. Workaround is based on mapping association as one-to-many and enforcing uniqueness in database by introducing unique constraints in custom database initializer.
If you're trying to achieve a 1-to-1 relationship, where there is at the most only one Second entity associated to a First entity, and where there is no reverse property try the following:
class First
{
[Key]
public Guid FirstId { get; set; }
}
class Second
{
[Key]
public Guid FirstId { get; set; }
public First First { get; set; }
}
class SecondMapping : EntityTypeConfiguration<Second>
{
public SecondMapping()
{
this.HasRequired(s => s.First);
}
}
You can however use a separate First_id column to do this kind of association, but then you would be effectively creating a 1-to-N relationship. It can be 'forced' to be 1-to-1 via a UNIQUE constraint, but you won't be able to create a reverse property due to a limitation in EF (as Ladislav mentioned):
class SecondMapping : EntityTypeConfiguration<Second>
{
public SecondMapping()
{
this.HasRequired(s => s.First).WithMany().HasForeignKey("First_id");
}
}

Categories