I am using entity framework code first to design a SQL database and "SEED" method to populate the database with initial data. Following are two models with one to many relationship. "Foo" can have many "FooSection"
public class Foo {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int FooId {get; set;}
// Some more properties.
// Navigation collection
public virtual ICollection<FooSection> fooSections {get; set;}
}
public class FooSection {
// Has composite key
[ForeignKey("foo"), Column(Order=1)]
public int FooId {get; set;}
[Key, Column(Order=2)]
public string SectionName {get; set;}
// Some more properties
// Navigation property
public virtual Foo foo {get; set;}
}
Say first instance of "Foo" with FooId = 1, has 2 "FooSection" and second instance of "Foo" with FooId = 2, has 1 "FooSection". So my seed method looks like this -
protected override void Seed(PDI.Cloud.Models.ApplicationDbContext context)
{
// First add data for "Foo"
var FooList = new List<Foo>();
if(!(context.Foos.Any()))
{
// First instance of Foo
FooList.Add(
new Foo{
// Assign values to properties here
}
);
// Second instance of Foo
FooList.Add(
new Foo{
// Assign values to properties here
}
);
FooList.ForEach(f => context.Foos.AddOrUpdate(f));
context.SaveChanges();
// Get info of "FooSection"
var fooSectList = getFooSectList(context);
// Assign "FooSection"s to respective "Foo".
FooList[0].fooSections.Add(fooSectList[0]); // Section 1 for Foo with id = 1
FooList[0].fooSections.Add(fooSectList[1]); // Section 2 for Foo with id = 1
FooList[1].fooSections.Add(fooSectList[2]); // Section 1 for Foo with id = 2
Context.SaveChanges();
}
}
private List<FooSection>getFooSectList(PDI.Cloud.Models.ApplicationDbContext context)
{
var FooSectList = new List<FooSection>();
if(!(context.FooSections.Any()))
{
// 1st FooSection for Foo with FooId = 1
FooSectList.Add(
new FooSection{
FooId = 1,
SectionName = "Sect1"
}
);
// 2nd FooSection for Foo with FooId = 1
FooSectList.Add(
new FooSection{
FooId = 1,
SectionName = "Sect2"
}
);
// 1st FooSection for Foo with FooId = 2
FooSectList.Add(
new FooSection{
FooId = 2,
SectionName = "Sect1"
}
);
FooSectList.ForEach(f => context.FooSections.AddOrUpdate(f));
context.SaveChanges();
}
return FooSectList;
}
When I try to run the seed method, it gives me SQLException "Violation of PRIMARY KEY constraint . Cannot insert duplicate key in object. The duplicate key value is (0, Sect1)"
Am I missing something here? Because I think for composite keys as far as combination is unique, I should not get such error.
I would appreciate any help.
Thanks.
I think you are expecting FooSection to have a composite primary key of FooId, SectionName. However, the way you have it now, your primary key is SectionName only.
You'll have to add the Key attribute to the FooId property to turn it into a composite primary key:
[Key, ForeignKey("foo"), Column(Order=1)]
public int FooId {get; set;}
Related
I am using EF code first approach and i have three types
public class A
{
public int Id {get; set;}
public C C {get; set;}
}
public class B
{
public int Id {get; set;}
public C C {get; set;}
}
public class C
{
public int Id {get; set;}
}
When I create an entry in B a new row in C is created as expected. I want to refer to the same row created by B in A. Something like below
A a = new A();
a.C = GetC(id);
dbContext.A.Add(a);
public C GetC(int id)
{
return dbContext.C.FirstOrDefault(c => c.id == id);
}
When i do the above a new entry in C is created. How can I avoid the new entry I want to use the same old entry created by B?
I have tried doing the following but it results in failure citing another entity of the same type already has the same primary key value.
A a = new A();
a.C = dbContext.C.FirstOrDefault(c => c.id == id);
dbContext.A.Add(a);
If you work in ef, then there should be foreign keys (as in the database), and if you add the foreign key to a, this should be enough for make relationships.
But it is in your example that you can use the following:
A a = new A();
a.C = dbContext.C.AsNoTracking().FirstOrDefault(c => c.id == id);
dbContext.A.Add(a);
After AsNoTracking - dbcontect will not cache and track it.
You can read more about it: https://learn.microsoft.com/en-us/dotnet/api/system.data.entity.dbextensions.asnotracking?view=entity-framework-5.0.0
I have two entities involved in a one-to-many relationship:
public class Voter
{
// VoterID and CountyID define a unique primary key
public int VoterID {get;set;}
public int CountyID {get;set;}
public ICollection<Contact> Contacts {get;set;}
}
public class Contact
{
public int ID {get;set;}
public string Data {get;set;}
// VoterID and CountyID define a unique foreign key
public int VoterID {get;set;}
public int CountyID {get;set;}
public Voter Voter {get;set;}
}
The composite primary key and foreign key relationship are defined in Context.OnModelCreating() as follows:
builder.Entity<Voter>().HasKey( x => new { x.CountyID, x.VoterID } );
// one to many relationship: voter -> contact
builder.Entity<Voter>()
.HasMany( v => v.Contacts )
.WithOne( c => c.Voter )
.HasForeignKey( c => new { c.CountyID, c.VoterID } );
builder.Entity<Contact>()
.HasOne( c => c.Voter )
.WithMany( v => v.Contacts )
.HasForeignKey( c => new { c.CountyID, c.VoterID } );
If I try to add a new Contact directly to the Contacts table, either like this:
_context.Contacts.Add(new Contact() {
VoterID = ...,
CountyID = ...,
Data = ....,
});
or like this:
Voter voter = _context.Voters.Single(...);
_context.Contacts.Add(new Contact() {
Voter = voter,
Data = ....,
});
the record doesn't get added. No error, no exception, just a silent fail.
However, adding the new Contact from the Voter instance works:
Voter voter = _context.Voters.Include(v=>v.Contacts).Single(...);
voter.Contacts = voter.Contacts ?? new List<Contact>();
voter.Contacts.Add( new Contact() {
Voter = voter,
Data = ....,
});
I don't understand why adding the new entity from the "one" side works but adding it from the "many" side doesn't.
Edit
In reviewing this post I realized, in simplifying the model, I may have inadvertently left out an important fact: the primary key, and hence the foreign key in Contact, is composite. I've updated the question to include these elements.
I have 2 subclasses inheriting from the same base class. They all map back to the same table. If field1 in the db table is null and field2 is not null, it's one subclass. If field1 is not null and field 2 is null, it's the other subclass.
I keep getting an error. The actual message is: "Invalid column name 'Discriminator'." It actually says Discriminator... I didn't throw that in as a generic term.
Here is a sample of my code:
DatabaseTableA
TableAId ( PK , int )
FooId ( FK , int , null )
BarId ( FK , int , null )
Prop1 ( int )
Prop2 ( int )
Prop3 ( int )
public abstract class BaseClass
{
public int Prop1{ get; set; }
public int Prop2{ get; set; }
public int Prop3{ get; set; }
}
public class Foo : BaseClass
{
public int FooId{get;set;}
}
public class Bar : BaseClass
{
public int BarId{get;set;}
}
internal class BaseClassMap : EntityTypeConfiguration<BaseClass>
{
public BaseClassMap()
{
ToTable("DatabaseTableA");
HasKey( e => e.TableAId);
Map<Foo>( m => m.Requires("BarId").IsDBNull());
Map<Bar>( m => m.Requires("FooId").IsDBNull());
}
}
How do I map this correctly?
Entity Framework will assume that any class that inherits from a POCO class that is mapped to a table on the database requires a Discriminator column, even if the derived class will not be saved to the DB.
See the solution here EF Code First "Invalid column name 'Discriminator'" but no inheritance
I have a project where I'm using EF5, I made a custom Guid Generator and I have an override of the SaveChanges method to assign the ids of my entities.
Everything is working fine except in one case: when the ID of one entity is a FK to another ID of another entity.
A little bit of code to explain the problem:
I have two entities I cannot change:
public class FixedEntityA
{
public Guid Id { get; set;}
public string SomeText { get; set; }
}
public class FixedEntityB
{
public Guid Id { get; set;}
public int OneInt { get; set; }
}
In my project I have an entity defined like this:
public class ComposedEntity
{
public Guid Id { get; set;}
public FixedEntityA FixedA { get; set; }
public FixedEntityB FixedB { get; set; }
public double OneDouble { get; set; }
}
The relationships are:
ComposedEntity may have 0 or 1 FixedEntityA
ComposedEntity may have 0 or 1 FixedEntityB
The constraints on the id are:
The Id of FixedEntityA is a FK pointing to the Id of ComposedEntity
The Id of FixedEntityB is a FK pointing to the Id of ComposedEntity
The mapping class are:
public ComposedEntity(): EntityTypeConfiguration<ComposedEntity>
{
HasOptional(fea => fea.FixedA).WithRequired();
HasOptional(feb => feb.FixedB).WithRequired();
}
Here is my SaveChanges override:
foreach (var entry in ChangeTracker.Entries<IEntity>().Where(e => e.State == EntityState.Added))
{
Type t = entry.Entity.GetType();
List<DatabaseGeneratedAttribute> info = t.GetProperty("Id")
.GetCustomAttributes(typeof (DatabaseGeneratedAttribute), true)
.Cast<DatabaseGeneratedAttribute>().ToList();
if (!info.Any() || info.Single().DatabaseGeneratedOption != DatabaseGeneratedOption.Identity)
{
if (entry.Entity.Id == Guid.Empty)
entry.Entity.Id = (Guid) _idGenerator.Generate();
}
}
return base.SaveChanges();
This code works fine everywhere for all kind of relationships except in this case, I am missing a test to make sure I'am not setting an id on id that are foreign keys, and I have no clue on how to check if an Id is a FK...
Here is a sample object where this code fails:
var fea = new FixedEntityA();
var feb = new FixedEntityB();
var composedEntity = new ComposedEntity();
composedEntity.FixedA = fea;
composedEntity.FixedB = feb;
If you insert the whole graph, all three objects are marked as Added and all Ids are default.
The problem is, with the current SaveChanges method, I will go through all object with the Added state in the change tracker and I will assign an Id to all entity with a default Guid and break my FK constraints.
Thanks in advance guys!
Here is some code that will get the FK properties for a given type (it's horrible I know). Should be simple enough to plug this into your code.
var typeName = "Category";
var fkProperties = ((IObjectContextAdapter)db)
.ObjectContext
.MetadataWorkspace
.GetItems<AssociationType>(DataSpace.CSpace)
.Where(a => a.IsForeignKey)
.Select(a => a.ReferentialConstraints.Single())
.Where(c => c.FromRole.GetEntityType().Name == typeName)
.SelectMany(c => c.FromProperties)
.Select(p => p.Name);
(This looks like a long question, but it's not really, honest!)
I am trying to get a simple proof of concept working with Entity Framework 4 and the CTP 3 version of Code Only. It feels like I'm missing something really obvious and simple.
I have this following test which is failing:
[TestFixture]
public class ParentChildTests
{
[Test]
public void ChildRead_DatabaseContainsRelatedObjects_ParentIsNotNull()
{
var ctx = GetMyObjectContext();
var child = ctx.Children.Where(c => c.Id == 1).Single();
var parent = child.ParentTable;
Assert.That(parent, Is.Not.Null);
}
// GetMyObjectContext etc...
}
The read of child works fine and I get back a ChildTable whose ParentTableId value is '1' as I would expect, but the ParentTable property is NULL. I do not expect this because my POCOs have all virtual properties (see below) and EF4 has lazy loading enabled by default.
What am I missing?
Database
create table parent_table
(
parent_table_id int identity(1,1) primary key,
parent_table_name varchar(50) not null,
display_name varchar(50)
)
create table child_table
(
child_table_id int identity(1,1) primary key,
child_table_name varchar(50) not null,
parent_table_id int not null
)
alter table child_table add constraint FK_child_table__parent_table
foreign key (parent_table_id) references parent_table(parent_table_id)
POCO Entities
public class ParentTable
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string DisplayName { get; set; }
}
public class ChildTable
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual int ParentTableId { get; set; }
public virtual ParentTable ParentTable { get; set; }
}
Entity Configurations
public class ParentTableConfiguration : EntityConfiguration<ParentTable>
{
public ParentTableConfiguration()
{
MapSingleType(pt => new
{
parent_table_id = pt.Id,
parent_table_name = pt.Name,
display_name = pt.DisplayName,
})
.ToTable("dbo.parent_table");
Property( pt => pt.Id ).IsIdentity();
Property( pt => pt.Name ).IsRequired();
}
}
public class ChildTableConfiguration : EntityConfiguration<ChildTable>
{
public ChildTableConfiguration()
{
MapSingleType(ct => new
{
child_table_id = ct.Id,
child_table_name = ct.Name,
parent_table_id = ct.ParentTableId,
})
.ToTable("dbo.child_table");
Property( ct => ct.Id ).IsIdentity();
Property( ct => ct.Name ).IsRequired();
Relationship(ct => ct.ParentTable)
.HasConstraint((ct, pt) => ct.ParentTableId == pt.Id);
}
}
(Thanks for reading this far!)
As far as understand you just do not load this navigation property.
This will result in eager loading.
var child = ctx.Children.Include("ParentTable").Where(c => c.Id == 1).Single();
Or you could enable lazy loading by setting ctx.ContextOptions.LazyLoadingEnabled = true;