I am trying to insert an object into a database table with Entity Framework and using code first (fluent api). Whilst doing this I keep running into one of the following errors:
1) InvalidOperationException: A dependent property in a
ReferentialConstraint is mapped to a store-generated column. Column:
'Id'
2) Cannot insert value into identity column with IDENTITY_INSERT set
to OFF
My relationship is a one-to-one however perhaps I can rework or structure the database to accomplish what I am wanting. I have also thought about utilizing a one to zero or zone even though the other object will always be required.
So I have the following database tables mapped into these C# objects (with virtual for the mapping):
public class test
{
[Key]
public long Id { get; set; }
public DateTime ResultDate { get; set; }
public virtual test_additional test_additional { get; set; }
public virtual test_status test_status { get; set; }
}
public class test_additional
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long TestId { get; set; } //Foreign Key to test
...
public virtual test test { get; set; }
}
public class test_status {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long TestId { get; set; } //Foreign Key to Test
public long TestFormId { get; set; } //this is the object I want to insert, Foreign key to the Primary key of test_form
...
public virtual test test { get; set; }
public virtual test_form test_form { get; set; } //object mapping
}
public class test_form {
[Key]
public long Id { get; set; } //Primary Key
public string FileName { get; set; }
public virtual test_status test_status { get; set; }
}
So some pretty simple objects, I've stripped members/columns that are necessary for the functionality for ease of readability.
So there are test objects that have an optional test_additional or test_status .
These are generated with a one to zero-or-one relationship. Which are working fine and I have the relationship defined as:
modelBuilder.Entity<test>()
.HasOptional(e => e.test_additional)
.WithRequired(e =>e.test);
modelBuilder.Entity<test>()
.HasOptional(e => e.test_status)
.WithRequired(e => e.test);
Now the entity I am having trouble with is the test_form, if a test_status is defined there should always be a test_form associated with that. I currently have a relationship defined as:
modelBuilder.Entity<test_form>()
.HasRequired(e => e.test_status)
.WithRequiredDependent(e => e.test_form);
In addition I have tried appending this config:
modelBuilder.Entity<test_status>()
.HasKey(e => e.TestFormId);
--
Here is a simple implementation of inserting this object in the database:
try {
test UserTest = new test { ResultDate = DateTime.Now; }
UOW.test.Insert(UserTest);
UOW.Save();
test_additional ta = new test_additional { TestId = UserTest.Id; }
test_form tf = new test_form { FileName = "Testing.pdf"; }
UOW.test_additional.Insert( ta );
UOW.test_form.Insert( tf );
UOW.Save(); //This is where it will throw that error.
test_status status = new test_status {
TestId = UserTest.Id;
TestFormId = tf.Id;
}
UOW.test_status.Insert( status );
UOW.Save();
} catch {
throw;
}
--
I have used BreakPoints before the Unit of Work saves and I can confirm that the Id in the test_form object is the default of long which is 0. So I am not setting the Identity Column explicitly. Upon removing of test_form (in the implemented method) I can insert into the test_additional category and save with no issue.
So my question is really... are my entity relationships defined correctly? Would it be smarter to use an additional One to Zero-or-One for the test_form object? Why can I not insert this simple object into my database?
I have also thought about defining the virtual test_form object in test_status as an ICollection, then I could use .HasMany(e => e.test_form).HasForeignKey(e => e.TestFormId); so it would bind to the Foreign Key even though I would only be using 1 item for the test_status.
Opinions? Am I close?
Thanks again for taking the time to read my question!
i had your problem. just do delete your database and migration files. after do it add the new migration to create the new database.
Related
According to How to add a parent record with its children records in EF Core the following should work:
Model Builder
mb.Entity<IdentifierTable>().HasKey("StockID");
mb.Entity<StockTable>().HasKey("StockID");
mb.Entity<ProductTable>().HasKey("IdentifierID");
mb.Entity<GroupsTable>().HasKey("GroupId");
mb.Entity<StockTable>()
.HasOne(x => x.Identifier)
.WithOne(y => y.Stock)
.HasForeignKey<IdentifierTable>(y => y.StockID);
mb.Entity<StockTable>()
.HasOne(x => x.Group)
.WithMany(y => y.Stock)
.HasForeignKey(y => y.GroupId);
mb.Entity<ProductTable>()
.HasOne<IdentifierTable>(x => x.Identifier)
.WithOne(y => y.Product)
.HasForeignKey<IdentifierTable>(y => y.IdentifierID);
base.OnModelCreating(mb);
Relevant parts of StockTable
public int StockID { get; set; }
public int? GroupId { get; set; }
[...]
public IdentifierTable Identifier { get; set; }
public GroupsTable Group { get; set; }
Relevant parts of IdentifierTable
public int IdentifierID { get; set; }
public ProductTable Product { get; set; }
public int StockID { get; set; }
public StockTable Stock { get; set; }
Relevant parts of ProductTable
public int? IdentifierID { get; set; }
public IdentifierTable Identifier { get; set; }
[...]
Relevant parts of GroupsTable
public int GroupId { get; set; }
public List<StockTable> Stock { get; set; }
The actual program
foreach (var item in contexts.originalContext.StockTable
.Include(x=>x.Group)
.Include(x=>x.Identifier)
.ThenInclude(y=>y.Product))
{
contexts.destinationContext.StockTable.Add(item);
contexts.destinationContext.SaveChanges();
}
However, when saving the changes, I get an exception:
violation of FOREIGN KEY constraint "FK_EST_IDENT_PROD" on table
"ProductTable" Foreign key reference target does not exist
I understood EF Core should add StockTable's child properties as well (IdentifierTable, ProductTable, and distinct GroupsTable). What am I doing wrong here?
The reason I'm using FluentAPI is because I'm working with a pre-made database, I can't edit table/column names, nor relationships. Could it be because IdentifierTable.StockID is both primary key and foreign key?
I think I might have found a clue: Checking a tentative migration, I found EF Core is creating two foreign keys alright. However, it is creating both on IdentifierTable. The relationships I aim for are:
One StockTable has one and only one IdentifierTable.
One IdentifierTable has one or none ProductTable.
One StockTable has one or none GroupsTable.
One GroupsTable has none, one, or multiple StockTable.
I think I might not be able to use this database with EF Core. I tried scaffolding the models database-first, and EF Core didn't manage to understand a single foreign key relation.
I have three layers of tables in an existing database and I'm trying to include the bottom level records when I get the middle level data... This should be a one to many relationship - for shipment x with product y there are z analysis results.
public class Shipment
{
[Key]
public int Id { get; set; }
public string ShipName { get; set; }
public DateTime ShipmentDate { get; set; }
}
public class ShipmentDetails
{
[ForeignKey ("ShipmentId")]
public int Id { get; set; }
[ForeignKey ("ProductId")]
public int ProductId { get; set; }
Public double Weight { get; set; }
public virtual ShippingAnalysis Analysis { get; set; }
}
public class ShipmentAnalysis
{
[ForeignKey ("ShipmentId")]
public int Id { get; set; }
[ForeignKey ("ProductId")]
public int TenantId { get; set; }
[ForeignKey ("MetricId")]
public int MetricId { get; set; }
Public double Result { get; set; }
}
I'm using the fluent api way of defining the composite primary keys.
modelBuilder.Entity<ShippingDetail>()
.HasKey(c => new { c.ShipmentId, c.ProductlId });
modelBuilder.Entity<ShippingAnalysis>()
.HasKey(c => new { c.ShipmentId, c.ProductId, c.MetricId });
I get the Shipping detail with the (one to many) analysis records.
var results = _context.ShippingDetail.Include(sd => sd.Analysis)
.Where(sd => sd.ShipmentId == id);
This does not return a result in postman, but through the browser returns malformed JSON. If I drop the include, it works fine.
The problem is not composite key, but navigation property (hence relationship definition). The navigation property at (one) side (when present) must be a collection and navigation property at (many) side should be reference - see Relationships - Definition of Terms.
According to
modelBuilder.Entity<ShippingDetail>()
.HasKey(c => new { c.ShipmentId, c.ProductlId });
modelBuilder.Entity<ShippingAnalysis>()
.HasKey(c => new { c.ShipmentId, c.ProductId, c.MetricId });
the relationship should be ShippingDetail (one) -> (many) ShippingAnalysis, hence
public virtual ShippingAnalysis Analysis { get; set; }
property of ShippingDetail must be
public virtual ICollection<ShippingAnalysis> Analysis { get; set; }
This should be enough for EF Core to determine the correct composite FK columns. But if you want to be hundred percent sure (being explicit never hurts), add the following fluent configuration:
modelBuilder.Entity<ShippingDetail>()
.HasMany(e => e.Analysis)
.WithOne() // make sure to specify navigation property if exists, e.g. e => e.NavProp
.HasForeignKey(e => new { e.ShipmentId, e.ProductId });
P.S. Remove all these [ForeignKey] data annotations. They do different things depending on whether they are applied on FK property or navigation property, and for sure don't do what you think, and sometimes may actually lead to unexpected behaviors. Based on my experience with EF Core relationships, either let EF Core conventions do their job, or use fluent API.
I am unable to defined 1:0..1 relationship in Entity Framework.
I have "PerfData" and "AttachmentData" entity.
Perf is parent entity and it has zero or one Attachment.
In code AttachmentData.CustomForeignKeyId is FK to PerfData.Id, but in database FK should have different name Attachment.PerfId is FK to PerfData.Id (this is due to some base class and inheritance that I am not describing here).
public class AttachmentData {
...
public Guid Id {get;set;}
public Guid CustomForeignKeyId{ get; set; } // this is FK to Perf.Id
public PerfData Perf { get; set; } // navigation property
}
and corresponding Configuration is
internal class AttachmentDataConfig : BaseConfig<AttachmentData>
{
public AttachmentDataConfig () : base("Attachment")
{
Property(x => x.CustomForeignKeyId)
.HasColumnName("PerfId");
HasRequired(o => o.Perf)
.WithMany()
.HasForeignKey(f => f.CustomForeignKeyId)
.WillCascadeOnDelete(true);
}
}
If I have only this, it works fine. Migration is generated properly. However, I also need to have navigation property on other side:
public class PerfData {
public Guid Id {get;set;}
public AttachmentData> Attachment { get; set; } //navigation property
}
If I add this navigation property, then EF creates new migration with new column Perf.Attachment_Id that is unnecessary, as FK is already defined within Attachment table.
EF does not support 1:0..1 relationship with explicit FK column at the dependent side (it considers such relationship to be 1:0..N). For 1:0..1 it uses a so called Shared Primary Key Associations in which the PK of the dependent side is also a FK to the principal side.
With that being said, in your model the CustomForeignKeyId is redundant. The correct (from EF perspective) model would be like this:
public class PerfData
{
public Guid Id { get; set; }
public AttachmentData Attachment { get; set; } //navigation property
}
public class AttachmentData {
...
public Guid Id { get; set;}
public PerfData Perf { get; set; } // navigation property
}
with the following configuration:
internal class AttachmentDataConfig : BaseConfig<AttachmentData>
{
public AttachmentDataConfig() : base("Attachment")
{
HasRequired(e => e.Perf)
.WithOptional(e => e.Attachment)
.WillCascadeOnDelete(true);
}
}
UPATE: If you want to keep the existing FK field and use 1:0..N relationship, then change the PerfData class navigation property
public AttachmentData Attachment { get; set; }
to
public ICollection<AttachmentData> Attachments { get; set; }
and inside the configuration
.WithMany()
to
.WithMany(e => e.Attachments)
I have a one-to-many relationship where I am trying to delete a set of instances on the "many" side, but keep getting the exception "Adding a relationship with an entity which is in the Deleted state is not allowed". This is Entity Framework 6.1.1.
The relationship is one-to-many from Teacher to Course. The two classes are defined as:
[Table("Course")]
public partial class Course {
public int Id { get; set; }
public int? TeacherId { get; set; }
public virtual Teacher Teacher { get; set; }
}
[Table("Teacher")]
public partial class Teacher
{
public Teacher()
{
Course = new HashSet<Course>();
}
public int Id { get; set; }
[Required]
[StringLength(50)]
public string TeacherName { get; set; }
public virtual ICollection<Course> Course { get; set; }
}
The code that tries to delete the courses, is part of an import: A set of courses is coming in, and the courses that are in the database but not part of the incoming courses, should be deleted from the database. (In addition, the courses that are part of the incoming set but not in the database, should be created, but this seems to work).
var existingCourses = ctx.Courses.ToList();
var toCreate = incomingCourses.Where(x => !existingCourses.Contains(x)).ToList();
var coursesToDelete = existingCourses.Where(x => !incomingCourses.Contains(x)).ToList();
ctx.Courses.RemoveRange(coursesToDelete);
ctx.SaveChanges(); // The exception occurs here
The incoming set of courses are parsed from an XML file to a DTO. Before comparing them to the existing courses, they are placed in a list as:
var incomingCourses = incomingDtos.Select(x => new Course
{
Teacher = new Teacher { TeacherName = x.TeacherNameFromXml }
}.ToList();
There are other properties on the Course entity that identifies the course, but I have not shown them here as I suppose they are irrelevant.
When debugging, I noticed that the Teacher property of the Courses that are being deleted are non-empty before the call to RemoveRange() but null afterwards.. So it seems that there is some kind of cascade delete taking place.
I have tried to remove all cascade deletes via my DbContext and also specifying the relationship there. This changes nothing.
public class MyDbContext : DbContext {
public MyDbContext() {}
public MyDbContext(string connectionString) {
Database.Connection.ConnectionString = connectionString;
}
public virtual DbSet<Teacher> Teacher { get; set; }
public virtual DbSet<Course> Courses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Entity<Teacher>()
.HasMany(x => x.Course)
.WithOptional(x => x.Teacher)
.HasForeignKey(x => x.TeacherId)
.WillCascadeOnDelete(false);
}
}
The comment from #LukasKabrt pointed me in the right direction. Instead of comparing the Course objects directly, I convert the existing courses to DTOs "temporarily" inside the LINQ and compare DTOs. Now I am able to delete all from coursesToDelete
var coursesToDelete = (
from e in existing
let dto = new CourseDto(e)
where !incomingDtos.Contains(dto)
select e).ToList();
First off, I'm new to the Entity Framework and am migrating an existing project from a database framework that I wrote myself so I have a fair amount of flexibility in the solution I choose.
From what I've researched so far everything appears to be set up correctly. However, when my database is constructed, the table for a helper class I wrote has no columns in it (outside of its primary key). The most simplified version of the classes are included below with their relationships defined in the fluent API.
Classes
public class Concept
{
public long ID { get; set; }
[Index(IsUnique = true), MaxLength(255)]
public string Name { get; set; }
}
public class Tag
{
public long ID { get; set; }
public virtual Content Subject { get; set; }
public virtual Concept Concept { get; set; }
}
public class Helper
{
public long ID { get; set; }
public virtual Content Subject { get; set; }
public virtual List<Tag> Instances { get; set; }
// Helper functionality
}
public class Content
{
public long ID { get; set; }
public virtual Helper Helper { get; set; }
public Content() { Helper = new Helper() { Subject = this }; }
}
Context
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Tag>()
.HasRequired(t => t.Concept);
modelBuilder.Entity<Tag>()
.HasRequired(t => t.Subject);
modelBuilder.Entity<Helper>()
.HasRequired(t => t.Subject)
.WithRequiredDependent(c => c.Helper);
modelBuilder.Entity<Helper>()
.HasMany(t => t.Instances);
modelBuilder.Entity<Content>()
.HasRequired(c => c.Helper)
.WithRequiredPrincipal();
base.OnModelCreating(modelBuilder);
}
Program.cs
static void Main(string[] args)
{
Content content = null;
using (var context = new Context())
{
content = context.Content.Find(1);
if (content == null)
{
content = new Content();
context.Content.Add(content);
context.Helper.Add(content.Helper);
context.SaveChanges();
}
}
}
It's also worth mentioning that when the data is saved, the Helper is assigned an ID but on loading the parent class (Content) the second time around, the Helper is not lazy loaded as I would expect from the 'virtual' keyword. I suspect that this is caused by the same issue causing the absence of data in the table.
I have tried both the data annotation and fluent API approaches that EF provides but it seems that there is something fundamental that I am misunderstanding. I would like to retain this helper class as it helps organize the code far better.
As I have spent a fair amount of time researching these relationships / APIs, and scouring Google / SO without found anything to solve this issue in particular any help would be greatly appreciated!
Updated: Solution
Thanks to a question in the comments, I realized that I was expecting to see the keys of a many-to-many relationship in the tables for the entity types themselves (i.e. in the Helpers table). However, in a many-to-many relationship, the keys will always be placed in a separate table (concatenation of type names) which was not being previously created.
By adding '.WithMany();' to the Helper section of the OnModelCreating function as below
modelBuilder.Entity<Helper>()
.HasMany(t => t.Instances)
.WithMany();
the many-to-many relationship became properly defined and the HelperTags table generated as expected. This is due to the fact that the many-to-many relationship is one way (Helpers always refer to Tags, Tags never refer to Helpers). This is also why the 'WithMany' does not have any arguments (since no Helper properties exist in the Tag class). Fixing this simple oversight solved the problem!
You are probably working harder than you need to in the on ModelCreate. You should probably redesign your classes use Identifiers, like this:
public class Tag
{
public long Id { get; set; }
public long SubjectId { get; set; }
public long ConceptId { get; set; }
public virtual Content Subject { get; set; }
public virtual Concept Concept { get; set; }
}
You need to keep the ID names the EXACT same as the object names + Id and EF will magically link everything up. If you don't want them required then make the id nullable (C# 6 == long? SubjectId).
Also, I have changed the ID -> Id; I have no idea if this matters. At one point I remember having to do that to get things working (it was YEARS ago) and I have been doing it that way ever since.
Consider reading:
Entity Framework Code First Conventions
relationship Convention
In addition to navigation properties, we recommend that you include
foreign key properties on the types that represent dependent objects.
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>
<principal class name><primary key property name>
<principal primary key property name>
If multiple matches are found then precedence is given in the order
listed above.
Foreign key detection is not case sensitive.
Sample Code from MSDN:
In the following example the navigation properties and a foreign key are used to define the relationship between the Department and Course classes.
public class Department
{
// Primary key
public int DepartmentID { get; set; }
public string Name { get; set; }
// Navigation property
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
// Primary key
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
// Foreign key
public int DepartmentID { get; set; }
// Navigation properties
public virtual Department Department { get; set; }
}