Entity Framework 0..1 to 0 relation - c#

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

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

How to create one-to-one relationship in Entity Framework Core?

Let's start with one-to-many relationship:
public sealed class MyContext : DbContext
{
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Slave>()
.HasOne(typeof(Master))
.WithMany() // *
.HasForeignKey(nameof(Slave.ForeignField));
}
}
So I declare that per one record in Master table I can have multiple records in Slave table. When I run EF tools to build migration this is accepted and works fine.
But when I change the line marked with asterisk to:
.WithOne()
in order to build one-to-one relationship building migration fails with error:
You are configuring a relationship between 'Slave' and 'Master' but
have specified a foreign key on 'ForeignField'. The foreign key must
be defined on a type that is part of the relationship.
I don't get it, just a second ago the given field (property in C# terms) was OK, and now EF claims it cannot find it?
Whad do I miss? How to make EF happy?
Record types are as follows -- please note there are no navigational properties.
internal sealed class Slave
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid InstanceId { get; set; }
public Guid ForeignField { get; set; }
}
internal sealed class Master
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid Id { get; set; }
}
For the time being I solved this using raw SQL, it works, but I am still curious what is wrong here.
Thanks to #AminGolmahalle answer, my curiosity was triggered why and can I use HasForeignKey in generic form. This lead me to finding out that I cannot, but what more is that WithOne and WithMany are not 1:1 replacements of each other. Both lead to different overloads.
So the first version one-to-many worked because I was hitting the right overload, the second didn't, because I was passing incorrect arguments. The correct version is:
builder.Entity<Slave>()
.HasOne(typeof(Master))
.WithOne()
.HasForeignKey(nameof(Slave), nameof(Slave.ForeignField)); // changed
the first argument has to be name of the slave table (again).
But is is even better to switch to generic version (see last comment under accepted answer) and avoid possibility of such "stupid" mistake in the first place.
Below code just sample for relation one to one:
public class Author
{
public int AuthorId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public AuthorBiography Biography { get; set; }
}
public class AuthorBiography
{
public int AuthorBiographyId { get; set; }
public string Biography { get; set; }
public DateTime DateOfBirth { get; set; }
public string PlaceOfBirth { get; set; }
public string Nationality { get; set; }
public int AuthorRef { get; set; }
public Author Author { get; set; }
}
You Can Use FluentApi For Relation In EntityFramework:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Author>()
.HasOne(a => a.Biography)
.WithOne(b => b.Author)
.HasForeignKey<AuthorBiography>(b => b.AuthorRef);
}
Using FluentApi is much better than DataAnnotaion.
FluentApi In Asp Core
I Suggest To You Read About FluentValidation
Refer this link, i think this will do
https://www.learnentityframeworkcore.com/configuration/one-to-one-relationship-configuration

Entity Framework Custom Navigational Properties

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

Entity Framework foreign key to multiple tables/entities

I need to implement Entity-Attribute-Value functionality on multiple data tables using Entity Framework. Let's say I have an attribute value EF class that looks like this:
public class EntityAttributeValue
{
// Not important to my question.
public virtual Entity ParentEntity { get; set; }
public virtual EntityAttribute ParentEntityAttribute { get; set; }
// Field in question.
public Guid ParentSurrogateKey { get; set; }
public string Value { get; set; }
...
}
Then I have multiple entities that have supplementary EAV values associated with them:
public class Entity1
{
// Key. EntityAttributeBalue.ParentSurrogateKey maps to this.
[Key]
public Guid SurrogateKey { get; set; }
// Standard properties.
public string Property1 { get; set; }
public string Property2 { get; set; }
// Collection of EAV values associated with this entity/table.
[ForeignKey("ParentSurrogateKey")]
public virtual IList<EntityAttributeValue> EntityAttributeValues { get; set; }
}
public class Entity2
{
// Key. EntityAttributeBalue.ParentSurrogateKey maps to this.
[Key]
public Guid SurrogateKey { get; set; }
// Standard properties.
public string OtherProperty1 { get; set; }
public string OtherProperty2 { get; set; }
// Collection of EAV values associated with this entity/table.
[ForeignKey("ParentSurrogateKey")]
public virtual IList<EntityAttributeValue> EntityAttributeValues { get; set; }
}
My problem is that both Entity1 and Entity2 have EntityAttributeValue objects associated with them. Code first migrations tries to create a foreign key from EntityAttributeValue back to Entity1 and another one back to Entity2 on ParentSurrogateKey. The surrogate key for any single given EntityAttributeValue is only associated with either one Entity1 or one Entity2 (or, expanding out, one EntityN...), not both/all.
I have a many to many relationship here, but one side not only maps to multiple rows, but multiple entities/tables over a shared GUID column.
How should I be approaching this? Should I just remove the EntityAttributeValue foreign keys back to Entity1 and Entity2 from the automatic migration (which would be a long term pain)? Should I be manually retrieving the list of EntityAttributeValues for a given EAV entity instead of relying on EF to do it for me?
Well, the answer turned out to be obvious and simple. I needed to define a many-to-many relationship with FluentAPI. In OnModelCreating, I just added:
modelBuilder.Entity<Entity1>()
.HasMany(m => m.EntityAttributeValues)
.WithMany();
modelBuilder.Entity<Entity2>()
.HasMany(m => m.EntityAttributeValues)
.WithMany();
I thought I had tried this, but I guess I hadn't. Because the many-to-many relationship creates an intermediate table for each entity and the foreign keys are on that intermediate table (and there is only a row in the intermediate table when a given EntityAttributeValue applies to a given Entity), no foreign key issues.

MVC Scaffolding and EF 'One To Zero or One' relationships error

In my AspNet MVC 3 project when I try to scaffold an entity which has a One to Zero or One relationship with another entity I get "An item with the same index has already been added" error.
Essentially this happens when the Primary Key of the related table is also a Foreign Key.
At the moment my workaround is
Add an Id column to the related table and make it the primary key
Add Unique Key to the Foreign Key Column.
The problem with this is that EF will generate an ICollection navigation property for the related entity instead of just a property of the related entity type (which I can set to null in case of zero related entities)
Is this a know bug?
Am I doing something wrong?
Is there a better work around to get rid of the ICollection navigation property?
See my answer on this question:
How do I code an optional one-to-one relationship in EF 4.1 code first with lazy loading and the same primary key on both tables?
That's the example code with the correct configuration.
public class ZoneMedia
{
public int ZoneMediaID { get; set; }
public string MediaName { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public virtual ZoneMediaText MediaText { get; set; }
}
public class ZoneMediaText
{
public int ZoneMediaID { get; set; }
public string Text { get; set; }
public int Color { get; set; }
public virtual ZoneMedia ZoneMedia { get; set; }
}
public class TestEFDbContext : DbContext
{
public DbSet<ZoneMedia> ZoneMedia { get; set; }
public DbSet<ZoneMediaText> ZoneMediaText { get; set; }
protected override void OnModelCreating (DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ZoneMedia>()
.HasOptional(zm => zm.MediaText);
modelBuilder.Entity<ZoneMediaText>()
.HasKey(zmt => zmt.ZoneMediaID);
modelBuilder.Entity<ZoneMediaText>()
.HasRequired(zmt => zmt.ZoneMedia)
.WithRequiredDependent(zm => zm.MediaText);
base.OnModelCreating(modelBuilder);
}
}
class Program
{
static void Main (string[] args)
{
var dbcontext = new TestEFDbContext();
var medias = dbcontext.ZoneMedia.ToList();
}
}
You can also achieve this with DataAnnotations, but I generally prefer to keep my entity models as POCOs.
Try to use the [Key] attribute to the intended primary key. You may need to import the namespace System.ComponentModel.DataAnnotations
Also check the documentation about the full implementation of this namespace.
http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.aspx

Categories