Code First and Parent-Child references - code-first

I'm using Code First CTP 5. I have a fairly simple setup between a parent table and child tables
Create table testA (
id int not null identity(1,1),
stuff varchar(200),
primary key (id)
);
go
create table testB (
id int not null
foreign key references testA(id),
morestuff varchar(200),
primary key (id)
);
go
To refer to these table using Code First, we have the following construct:
namespace Test.Models
{
public class TestEntities : DbContext
{
public DbSet<testa> testa { get; set; }
public DbSet<testb> testb { get; set; }
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<testa>().ToTable("testa");
modelBuilder.Entity<testa>()
.Property(p => p.id)
.HasDatabaseGenerationOption(DatabaseGenerationOption.Identity);
modelBuilder.Entity<testb>().ToTable("testb");
}
}
public class testa
{
public int id { get; set; }
public String stuff { get; set; }
public virtual testb testb { get; set; }
}
public class testb
{
public int id { get; set; }
public string morestuff { get; set; }
public virtual testa testa { get; set; }
}
}
When I try to add a record to testa, I get the error "Cannot insert explicit value for identity column in table 'testA' when IDENTITY_INSERT is set to OFF."
Ok. Strike 1 to Code First for not recognizing that Id is an identity column. We can fix this, so we tell CodeFirst that testa.id is an identity:
modelBuilder.Entity<testa>()
.Property(p => p.id)
.HasDatabaseGenerationOption(DatabaseGenerationOption.Identity);
That done we run it again and get another error: "A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'id'". So - what's wrong with this picture?
What am I doing wrong and how do I fix it???

In a 1:1 association, Code First recognize one of the entities as principal and the other one as dependent. Then it makes the principal PK as identity and you need to take care of a valid unique PK when inserting into the dependent table. In your case it picks testb as the principal but it looks like that you want testa to be the principal end in this association. This could be achieved by using fluent API and basically giving hint to Code First about which one is principal and which one is dependent:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<testb>()
.HasRequired(b => b.testa)
.WithOptional(a => a.testb);
}
For more information, take a look at this article:
Associations in EF Code First CTP5: Part 2 – Shared Primary Key Associations

Related

Ef core and multiple parent entities use list of the same child entity?

Is there an easy way to have a setup like this in EF Core?
ProjectEntity
Id
Name
List<Notes>
CustomerEntity
Id
Name
List<Notes>
NotesEntity
Id
Date
Note
Every parent entity would have a one-to-many relation to same child entity. So I can not use normal behavior as
NotesEntity
Id
ParentId
Date
Note
I have some idea to have like above but also add one field that said what the parent entity is, is that the right way to do it or is there a better way? If I use this way I can't use EF Core normal behavior with one-to-many relationship? I need to make more manual work for search / add and so on?
Edit :
Entity Framework multiple parent tables I found this solution, but there I need to make a connection from my child to every parent I use, it could be alot of them.
Did also find a solution like :
BaseEntity
List<Notes>
ProjectEntity:BaseEntity
NotesEntity
Id
BaseEntityId
...
This last solution maybe is the best way to do it if I have alot of parent entities?
[EDIT 220922]
Could [Owned] type has collection of other Items? Or this feature won't work on owned entitys? I guess this behavior isn't supported?
[Owned]
public class Note
{
public int Id { get; set; }
public string Text { get; set; }
public int UserId { get; set; }
public User User { get; set; }
public ICollection<string> Tags { get; set; }
}
I got an error on ICollection-row when I try to add-migration.
Unabel to determine the relationshop represented by navigation ... of
typ 'ICollection' Either manually configure the relationship, or
ignore this property using the '[NotMapped]' attribute.....
Maybe I could have one middleentity like :
public class NoteTagsEntity
{
public int Id { get; set; }
public ICollection<string> Tags { get; set; }
}
And then :
[Owned]
public class Note
{
public int Id { get; set; }
public string Text { get; set; }
public int UserId { get; set; }
public User User { get; set; }
public int NoteTagsId { get; set; }
public NoteTagsId NoteTagsId { get; set; }
}
Edit
I solved the Note functionality with having more FK's, one that point to Id of parent and one FK Id that point to what module that use that particular note. Here I don't have parent - child relation in my entities, I need to do this connection by myself but in this way it's easy to apply more modules that use note's later.
Use Owned Entity Types, and each entity will get its own notes table.
eg
public abstract class Entity
{
public int Id { get; set; }
}
public abstract class EntityWithNotes: Entity
{
public virtual ICollection<Note> Notes { get; set; }
}
[Owned]
public class Note
{
public int Id { get; set; }
public string Text { get; set; }
}
public class Project : EntityWithNotes
{
public string Name { get; set; }
}
public class Customer : EntityWithNotes
{
public string Name { get; set; }
}
creates
CREATE TABLE [Customer_Notes] (
[Id] int NOT NULL IDENTITY,
[CustomerId] int NOT NULL,
[Text] nvarchar(max) NOT NULL,
CONSTRAINT [PK_Customer_Notes] PRIMARY KEY ([CustomerId], [Id]),
CONSTRAINT [FK_Customer_Notes_Customer_CustomerId] FOREIGN KEY ([CustomerId]) REFERENCES [Customer] ([Id]) ON DELETE CASCADE
);
CREATE TABLE [Project_Notes] (
[Id] int NOT NULL IDENTITY,
[ProjectId] int NOT NULL,
[Text] nvarchar(max) NOT NULL,
CONSTRAINT [PK_Project_Notes] PRIMARY KEY ([ProjectId], [Id]),
CONSTRAINT [FK_Project_Notes_Project_ProjectId] FOREIGN KEY ([ProjectId]) REFERENCES [Project] ([Id]) ON DELETE CASCADE
);

EF6 extending an TPH hierarchy (mixing TPH and TPT)

I have a table used to store several inherited entities in TPH configuration.
That works well and there are no issues over that.
The issue I'm facing is that I need to extend some of those entities with additional fields and want those new fields stored in its own table using TPT.
To put some context I will give you an example:
The TPH stores a root PERIOD class and several inherited ones like QUARTER, MONTH, WEEK, etc, using a discriminator field.
So now I need to create a special QUARTER with some additional fields and want to store those additional field in its own table.
Is that possible in EF? I'm using EF 6.1 and haven't found a working sample or explanation on how to accomplish this specific scenario.
Thanks in advance,
Andrés.
public abstract class Period
{
public int Id { get; set; }
public string DisplayName { get; set; }
}
public class Month : Period {
public byte MonthValue { get; set; }
}
public class Quarter : Period {
public byte QuarterValue { get; set; }
}
public class SpecialQuarter : Quarter {
public int SpecialQuarterValue { get; set; }
}
public class TestContext : DbContext {
public DbSet<Period> Periods { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Period>().ToTable("Period");
// TPH
modelBuilder.Entity<Month>().Map(p => p.Requires("PeriodType").HasValue("M"));
modelBuilder.Entity<Quarter>().Map(p => p.Requires("PeriodType").HasValue("Q"));
//TPT
modelBuilder.Entity<SpecialQuarter>().Map(p => p.ToTable("SpecialQuarter"));
}
}
This context maps to these tables.
CREATE TABLE [dbo].[Period] (
[Id] [int] NOT NULL IDENTITY,
[DisplayName] [nvarchar](max),
[MonthValue] [tinyint],
[QuarterValue] [tinyint],
[PeriodType] [nvarchar](128) NOT NULL,
CONSTRAINT [PK_dbo.Period] PRIMARY KEY ([Id])
)
CREATE TABLE [dbo].[SpecialQuarter] (
[Id] [int] NOT NULL,
[SpecialQuarterValue] [int] NOT NULL,
CONSTRAINT [PK_dbo.SpecialQuarter] PRIMARY KEY ([Id])
)

How to correctly build EF5 Code-first model with two or more lists that relate to the same child table?

I have the following models:
public class SomeForm
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public IList<FacilityContactInformation> OriginatingFacilities { get; set; }
public IList<FacilityContactInformation> DestinationFacilities { get; set; }
}
public class FacilityContactInformation
{
public FacilityContactInformation()
{
Id = -1;
}
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[MaxLength(50)]
public string Owner { get; set; }
}
I am using automatic migrations to gen and re-gen the database schema.
This generates the error "Foreign key 'FK_dbo.FacilityContactInformations_dbo.SomeForm_SomeForm_Id ' references invalid column 'SomeForm_Id' in referencing table 'FacilityContactInformations'.
Could not create constraint. See previous errors.
I suspect the root cause is that EF tries to generate a FK FK_dbo.FacilityContactInformations_dbo.SomeForm_SomeForm_Id for both lists
Is there any way to keep using automatic migrations, but get this to generate a FK that works? It would seem like the FK should include the list name and generate two properties on FacilityContactInformations OR should generate an intermediate table to join on.
When you have 2 navigational properties that link to the same class, you should override OnModelCreating method of your dbcontext class. Then add this code into the OnModelCreating:
modelBuilder.Entity<SomeForm>
.Hasmany<FacilityContactInformation>(x => x.OriginatingFacilities);
modelBuilder.Entity<SomeForm>
.Hasmany<FacilityContactInformation>(x => x.DestinationFacilities);
This is because EF cannot determine the correct keys if the nav. prop. link to the same class.

Will one-to-one foreign key associations be supported in EF v-Next?

I have entities A and B and I want to create 2 distinct 1-1 associations between A and B. A should play the role as principal. Like this:
public class A
{
public int Id {get; set;}
public B B1 {get; set;}
public B B2 {get; set;}
}
public class B
{
public int Id {get; set;}
}
Since EF does not support one-to-one foreign key associations I cannot create a working model/database with EF. To my this sounds like a serious limitation. Are there any plans to support such associations in an upcoming version of EF?
What is the best workaround for to get this working. I know about creating two one-2-many associations. However, that would make B the principal and gives me problems with cascading deletes.
Thanks for replying to my question. Below is an example of what I want to do, i.e., create two (or more) 1-to-1 associations between an entity A and another entity B. Is this something that EF could support in vNext, or else, why would it be a bad idea?
Thanks again,
Merijn
public class A
{
public int Id {get; set;}
public int B1_Id {get; set;}
public B B1 {get; set;}
public int B2_Id {get; set;}
public B B2 {get; set;}
}
public class B
{
public int Id {get; set;}
}
public class SampleContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<A>().HasKey(c => c.Id);
modelBuilder.Entity<B>().HasKey(c => c.Id);
modelBuilder.Entity<A>().HasRequired(c => c.B1).WihOptional().ForeignKey(x=>x.B1_Id);
modelBuilder.Entity<A>().HasRequired(c => c.B2).WihOptional().ForeignKey(x=>x.B2_Id);
}
}
If "v-Next" is Entity Framework 6, then no, it apparently won't support one-to-one foreign key associations, as you can see on the roadmap for all features planned for EF 6.
You can also see that Unique Constraint support is not on the roadmap and still marked as "Under Review" on UserVoice.
Because a one-to-one foreign key association is basically a one-to-many association with a unique constraint on the foreign key column I would expect that one-to-one FK associations won't be implemented before Unique Constraint support is available. It's especially required if you want that A is the principal in your two relationships. Currently EF does not support relationships where the principal's key is not the primary key but some column with unique constraint.
In this blog post the feature is described and mentioned that it is "postponed", so let's hope for EF 7.
Perhaps it is a terminology issue.
In Code first EF, EF doesnt allow you to have 1:1 relationships with Principal and Dependent both with foreign keys to each other
or with the dependent having its own primary key unrelated to Principal.
With your example it looks like that it is a case of 2 navigation properties required.
And strictly speaking it is not 1:1. since you have 2 relationships to the same table.
you have 2 relationships of type 1:1.. EF sees this as many to 1.
If you have a true 1:1 relationship, EF will want the dependent to have the same Primary Key as the primary.
You can define Multiple NAVIGATION properties on Both Principle and dependent, which result in indexes.
So you may wish to investigate Many to 1 configurations
If you want the Primary to have an OPTINAL Foreign Key at DB level, You would need to ADD this FK later during migration or with script.
But arguably this is best seen as business logic/rule check rather than an OPTIONAL FK on principal.
So yes there are limitations in matching exactly what is possible on the DB.
But it is questionable is actually necessary in a code first scenario.
Neat trick here btw is to model in DB exactly what you want on Code first.
There use the EF Powertool nuget to reerse engineer Codefirst from DB.
EG mini DB with just the desired table relationships.
make a new project in Solution. Install Entity Framework Powertools.
Then use right click option in new project to "reverse engineer code first from DB".
It shows how to build that in code first if it can.... :-)
What I think you wanted to achieve... see code sample (sorry if I misunderstood the point your are making) code should execute if NUGET is loaded
using System.Data.Entity;
namespace EF_DEMO
{
class FK121
{
public static void ENTRYfk121(string[] args)
{
var ctx = new Context121();
ctx.Database.Create();
System.Console.ReadKey();
}
}
public class Main
{
public int MainId { get; set; }
public string BlaMain { set; get; }
public int? Sub1Id { set; get; } // Must be nullable since we want to use EF foreign key
public int? Sub2Id { set; get; } // Must be nullable since we want to use EF foreign key
public virtual Sub Sub1 { get; set; } // Reverse navigation
public virtual Sub Sub2 { get; set; } // Reverse navigation
// you may also need
public virtual ICollection<Sub> Subs { get; set; }
}
public class Sub
{
public int SubId { get; set; } // Deliberately DIFFERENT KEY TO MAIN.... not 1:1 so this is possible
public string blasub { set; get; }
public int MainId { set; get; } //set in API , this the FK
public virtual Main Main { get; set; } // van to Principal
}
public class Context121 : DbContext
{
static Context121()
{
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<Context121>());
}
public Context121()
: base("Name=Demo") { } // webconfig required to match
public DbSet<Main> Mains { get; set; }
public DbSet<Sub> Subs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Main>().HasKey(t => t.MainId)
.HasOptional(t => t.Sub1)
.WithMany()
.HasForeignKey(t=>t.Sub1Id) ; // tell EF the field is in POCO, use this please, otherwise it will create it.
modelBuilder.Entity<Main>()
.HasOptional(t => t.Sub2).WithMany()
.HasForeignKey(t=>t.Sub2Id);
modelBuilder.Entity<Sub>()
.HasKey(t => t.SubId)
.HasRequired(q => q.Main)
.WithMany()
.HasForeignKey(t => t.MainId);
}
}
}
WEBCONFIG....
<connectionStrings>
<add name="Demo" connectionString="Data Source=localhost;Initial Catalog=Demo;Integrated Security=True;MultipleActiveResultSets=True;App=EntityFramework"
providerName="System.Data.SqlClient" />
</connectionStrings>
Explain what problem do you need to resolve? This is sample of one-to-one mapping in EF 5.0
class Program
{
static void Main(string[] args)
{
using (var context = new SampleContext())
{
var mainEntity = new MainEntity();
mainEntity.DetailEntity = new DetailEntity();
context.SaveChanges();
}
}
}
public class SampleContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MainEntity>().HasKey(c => c.Id);
modelBuilder.Entity<DetailEntity>().HasKey(c => c.Id);
modelBuilder.Entity<MainEntity>().HasOptional(c => c.DetailEntity).WithRequired(p => p.MainEntity);
modelBuilder.Entity<DetailEntity>().HasRequired(c => c.MainEntity).WithOptional(p => p.DetailEntity);
}
public virtual DbSet<MainEntity> MainEntities { get; set; }
public virtual DbSet<DetailEntity> DetailEntities { get; set; }
}
public class MainEntity
{
public int Id { get; set; }
public DetailEntity DetailEntity { get; set; }
}
public class DetailEntity
{
public int Id { get; set; }
public MainEntity MainEntity { get; set; }
}

creating strict 1 to 1 relationship by code first

I want to create a 1 to 1 relationship by code first, below is my code,
class Person
{
public int id { get; set; }
public string Name { get; set; }
public virtual PersonDetail detail { get; set; }
}
class PersonDetail
{
public int id { get; set; }
public double Height { get; set; }
public double Weight { get; set; }
public virtual Person person { get; set; }
}
class EFTest : DbContext
{
public DbSet<Person> personSet { get; set; }
public DbSet<PersonDetail> detailSet { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>().HasRequired(x => x.detail).WithRequiredPrincipal(x => x.person);
}
}
But I still can insert a person without person detail. I'm trying to create a 1 to 1 relationship in model first, it works well, if I insert one end without the other end, there will be an exception thrown. Why code first with the code above create a 1 to 0..1 relationship?
Anyone can help?
That is possible only if both Person and PersonDetail will be mapped to the same table (the mapping technique is called Table Splitting) because strict 1:1 means that you cannot insert Person without existing PersonDetail but you also cannot insert PersonDetail without existing Person => you cannot insert either of them because the dependency will be always missing (remember each record has its own insert command and database check the integrity after each command not after transaction).
Only when you use table splitting EF will create single insert command containing data from both entities. In your entity model it will look like two entities with 1:1 mapping but in database it will be single table.