Reputation: 1042
We need to define a One-To-One relationship between following entities:
public class Foo
{
[Key, Column("Foo_ID")]
public int Id { get; set; }
public Bar Bar { get; set; }
}
public class Bar
{
[Key, Column("Bar_ID")]
public int Id { get; set; }
[Column("Bar_Foo_ID")]
public int? FooId { get; set; }
public Foo Foo { get; set; }
}
We need to migrate a legacy software and have both of them running side by side for now. So we can't change any relations. Unfortunately there are no foreign keys defined in the database.
modelBuilder.Entity<Foo>()
.HasOptional(a => a.Bar)
.WithRequired(x => x.Foo)
.WillCascadeOnDelete(true);
When we query Foo
and Include(x => x.Bar)
it creates a SQL query with LEFT OUTER JOIN bars ON Foo_ID = Bar_ID
which is wrong.
We need to change this to a LEFT OUTER JOIN bars on Foo_ID = Bar_Foo_ID
, but I'm not sure how Entity Framework supports that as we don't have foreign keys in our DB and Bar_ID
as PrimaryKey.
I know that there can be potentially more than one Bars
for one Foo
, but is there a way to enforce a One-To-One relationship?
When a Foo
is created, a Bar
is always created for it.
Upvotes: 1
Views: 1644
Reputation: 13458
For legacy data, you can use the following:
1 : 0-1 relationships are basically a special form of 1 : many relationships.
So you can configure the relation as 1:many in entity framework and potentially add a unique index to the foreign key property to enforce your constraint of max. one related entry.
public class Foo
{
[Key, Column("Foo_ID")]
public int Id { get; set; }
// this can't be a single reference property unfortunately... but it will only contain 0 or 1 object when working with it.
public ICollection<Bar> Bars { get; set; }
}
public class Bar
{
[Key, Column("Bar_ID")]
public int Id { get; set; }
[Index(IsUnique = true)]
[Column("Bar_Foo_ID")]
public int? FooId { get; set; }
public Foo Foo { get; set; }
}
modelBuilder.Entity<Foo>()
.HasMany(a => a.Bars)
.WithRequired(x => x.Foo)
.HasForeignKey(x => x.FooId)
.WillCascadeOnDelete(true);
Upvotes: 4
Reputation: 205709
The way I understand it, you have an existing column called Bar_Foo_ID
in Bar
table which has to be used as FK to the PK Bar_ID
column in Bar
table.
It's possible, but EF6 has limited support for such relationship, in particular does not support explicit FK property. So you have to remove the FooId
property from the model:
public class Bar
{
[Key, Column("Bar_ID")]
public int Id { get; set; }
public Foo Foo { get; set; }
}
and use the following fluent configuration:
modelBuilder.Entity<Foo>()
.HasOptional(a => a.Bar)
.WithOptionalPrincipal(x => x.Foo)
.Map(m => m.MapKey("Bar_Foo_ID"))
.WillCascadeOnDelete(true);
MapKey
is to specify the FK column name. WithOptionalPrincipal
is to specify that it's nullable (while WithRequired
would be for non nullable).
Note that EF6 treats cascade delete differently for optional relationships. Instead of deleting the associated records, it disassociates them by setting the FK to null
. In case you want to actually delete the associated Bar
when deleting the Foo
, you have to do that manually with code.
Upvotes: 1