Entity Framework WebAPI Model UpSerts - entity-framework

I'm having problems with duplicate data during migration with Code First.
A new foreign key record is duplicated each time the migration creates the master record.
The schemas in the database are being created correctly. Namely the Primary Keys and Foreign Key values (the latter being automatically generated)
Can someone please advise thanks about how I detach the foreign key record during migration to prevent it recreating the record or any configuration I need to implement? I've tried updating the state of the foreign key obects before inserting master data. to both modified and detached.
For example I see multi records for the same priority where there should only be 3.
I'm using Entity Framework 6.0.
public class VeloPointDbConfiguration : DbMigrationsConfiguration<VeloPointDbContext>
{
public VeloPointDbConfiguration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
protected override void Seed(VeloPointDbContext context)
{
context.TaskPriorities.AddOrUpdate(EventTaskPriority.Migrations.All());
context.TaskStatuses.AddOrUpdate(TaskStatus.Migrations.All());
EventOrganiserTask.Migrations.All().Select(x => context.Entry(x.Priority).State == EntityState.Modified);
EventOrganiserTask.Migrations.All().Select(x => context.Entry(x.TaskStatus).State == EntityState.Modified);
context.Tasks.AddOrUpdate(EventOrganiserTask.Migrations.All());
}
}
The following examples of the instances i'm using for the data.
I create the following methods for the foreign key objects
public static EventTaskType[] All()
{
return new[]
{
GetDeadline(),
GetEmail(),
GetTelephone(),
GetAppointment(),
GetSnailMail(),
};
}
internal static EventTaskType GetDeadline()
{
return new EventTaskType("09974722-D03E-4CA3-BF3A-0AF7F6CA1B67", 1, "Deadline")
{
Icon = ""
}
}
I call the following methods the create the master data.
public static EventOrganiserTask[] All()
{
return new EventOrganiserTask[]
{
GetBookHQ(1, new DateTime(Event.Migrations.EventDate.Year - 1, 10, 1)),
GetFindSponsor(2, new DateTime(Event.Migrations.EventDate.Year - 1, 10, 1)),
GetRegisterEvent(3, new DateTime(Event.Migrations.EventDate.Year - 1, 10, 1)),
GetBookFirstAid(4, Event.Migrations.EventDate.AddMonths(-6))
};
}
NOTE: When creating the master record, I call the method in the foreign key classes each time - which is the crux of the problem where I need to instruct the migration to detach this item.
public static EventOrganiserTask GetRegisterEvent(int id, DateTime date)
{
return new EventOrganiserTask
{
id = id,
Title = "Register event",
Summary = "Register the road race with the region",
DueDate = date,
Priority = EventTaskPriority.Migrations.GetHighPriority(),
Person = Person.Migrations.GetRaceOrganiser(1),
TaskType = EventTaskType.Migrations.GetDefault(),
TaskStatus = TaskStatus.Migrations.GetDefault(),
};
}
NOTE: When I do make changes to the data from the application, the foreign keys are not being updated. This must be related and indicates my entities are not configured correctly.
LATEST:
I'm still tearing my hair out. I've investigated this further and read about the migrations being multi threaded (It was another thread on stackoverflow but I can't find it again). Indeed running the Seed method I supposed is what it says on the tin and is purley for seeding data, so the data is only being added (regardless of AddOrUpdate - what's that all about then) So I've looked at the behaviour regarding the records being created. First of all I called context.SaveChanges() after creating the look up tables. At this point it doesn't created any duplicates as the items are only referenced once. I then let the seed method run the master data - but argggh - I see duplicates (when the instances are called on the master data). But this did flag something up with regard to the order in which it creates the records.
My next step was to create two migrations, but without any success.
I'm hoping somebody picks up this thread soon. I'm tearing my hair out.

Ok so i've finally found my answer. It was clever enough to create the foreign key relationships from the model, but i needed to be explicitly set the foreign key id field. I chose the Fluent API to explicitly set the relationships and I set the value of the id field in the mapping of the object.
modelBuilder.Entity<Task>()
.HasRequired(x => x.Priority)
.WithMany(x => x.Tasks)
.HasForeignKey(x => x.Priority_id);
Here it is in the seed method
public class VeloPointDbConfiguration : DbMigrationsConfiguration<VeloPointDbContext>
{
public VeloPointDbConfiguration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
protected override void Seed(VeloPointDbContext context)
{
context.TaskPriorities.AddOrUpdate(EventTaskPriority.Migrations.All());
context.TaskStatuses.AddOrUpdate(TaskStatus.Migrations.All());
EventOrganiserTask.Migrations.All().Select(x => context.Entry(x.Priority).State == EntityState.Modified);
EventOrganiserTask.Migrations.All().Select(x => context.Entry(x.TaskStatus).State == EntityState.Modified);
context.Tasks.AddOrUpdate(EventOrganiserTask.Migrations.All());
// Foreign Key relationships
modelBuilder.Entity<EventOrganiserTask>()
.HasRequired(x => x.TaskStatus)
.WithMany(x => x.Tasks)
.HasForeignKey(x => x.TaskStatus_id);
modelBuilder.Entity<Task>()
.HasRequired(x => x.TaskType)
.WithMany(x => x.Tasks)
.HasForeignKey(x => x.TaskType_id);
modelBuilder.Entity<Task>()
.HasRequired(x => x.Priority)
.WithMany(x => x.Tasks)
.HasForeignKey(x => x.Priority_id);
}
}

Related

The instance of entity type cannot be tracked with a composite primary key

I'm getting this error, but I can't seem to find a proper resolution.
The instance of entity type 'Assignment' cannot be tracked because
another instance with the same key value for {'OwnerUserId'} is
already being tracked. When attaching existing entities, ensure that
only one entity instance with a given key value is attached.
Functionally all I'm trying to do is insert some records with a composite primary key. Basically I just need to assign users to each other. I'm telling the context (afaik) to not do tracking, yet the error still seems to happen.
using (var context = new MyDbContext())
{
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
//Get students with no assignments
var missingAssignments = model.Students.Where(x => x.AssignedToRead.Count < 2);
foreach (var s in missingAssignments)
{
var need = 2 - s.AssignedToRead.Count();
List<Guid> assigned = new List<Guid>();
assigned.AddRange(s.AssignedToRead.Select(x => x.UserId));
try
{
for (var i = 0; i < need; i++)
{
//Get a random reader (who's not them)
var shuffle = model.Students.Where(x => x.UserId != s.UserId).Where(x => !assigned.Contains(x.UserId)).ToList();
shuffle.Shuffle();
var rando = shuffle.FirstOrDefault();
var newAssignment = new Assignment() {
OwnerUserId = s.UserId,
ReaderUserId = rando.UserId
};
assigned.Add(rando.UserId);
context.Add(newAssignment);
context.SaveChanges();
}
}catch(Exception ex)
{
//Handle
}
}
}
I've validated I'm not inserting duplicate records (like 2 objects with the same PKs)
Is the problem in the mapping maybe?
modelBuilder.Entity<Assignment>(entity =>
{
entity.HasKey(e => new { e.OwnerUserId, e.ReaderUserId });
entity.ToTable("assignments");
entity.Property(e => e.OwnerUserId).HasColumnName("ownerUserId");
entity.Property(e => e.ReaderUserId).HasColumnName("readerUserId");
entity.Property(e => e.ModifiedDate)
.HasColumnName("modifiedDate")
.HasDefaultValueSql("(getdate())");
entity.HasOne(d => d.Owner)
.WithOne()
.HasPrincipalKey<Assignment>(d => d.OwnerUserId)
.HasForeignKey<AspnetUsers>(d => d.UserId)
.HasConstraintName("FK_users_owner");
entity.HasOne(d => d.Reader)
.WithOne()
.HasPrincipalKey<Assignment>(d => d.ReaderUserId)
.HasForeignKey<AspnetUsers>(d => d.UserId)
.HasConstraintName("FK_users_reader");
});
I don't understand why it would be tracking when I tell it not to
EDIT It appears the issue is with my Navigation Property mapping on the context? If i remove that entirely the Insert works fine, and I still have functioning Nav Properties on the object... I'm so confused, I thought it had to be told what to do?
I believe any entity added using Add will be tracked, the tracking behaviour is for entities that are queried from the DB. Does the error happen on the first instance you add, or on the second, or later?
I suspect the issue is probably with the HasOne.WithOne mapping between Assignment and User. I think this should be a HasOne.WithMany as a One-to-One typically defaults to being between PKs. (Though /w EF Core I believe you can nominate a non-PK 1-to-1) This mapping may be overruling the PK definition or otherwise adding a unique requirement that the tracking is considering. One User can have many assignments, but each assignment has one owner.
Another thing to check: In your entity class definition for Assignment, do you happen to have any [Key] attributes defined that might be tripping up the EF mapping? Your modelBuilder key mapping otherwise looks fine.

Adding new records into EF fails with NULL column error

I need to load records from an existing database into a new database using a LINQ query using LINQPad. If the record exists in the database, then update the name. Otherwise, insert the record. Currently, the new DB is empty. This script will run periodically, so I have to check for updates. I have code that loads the existing and new records into two List for comparison. I loop through the list:
foreach (Coaster oldCoaster in listOfOldCoasters) {
var coaster = listOfNewCoasters.Where(c => c.coasterId == oldCoaster.coasterId).FirstOrDefault();
if (coaster != null) {
coaster.Name = oldCoaster.Name;
} else {
coaster = new Models.Coaster();
coaster.CoasterId = oldCoaster.coasterId;
coaster.Name = oldCoaster.Name;
//newCoasterDbContext.Coasters.Attach(coaster);
newCoasterDbContext.Coasters.Add(coaster);
}
}
newCoasterDbContext.SaveChanges();
When I run the code using the "Add" method, I receive the exception "OriginalValues cannot be used for entities in the Added state." Digging deeper, I see this message:
Cannot insert the value NULL into column 'CoasterId', table 'Coaster'; column does not allow nulls.
I am setting the primary key in question, so I must be missing something about EF as to why this fails. If I uncomment the "Attach" statement and try that instead of "Add", then the script runs, but nothing gets inserted into the database.
My Coaster class:
public class Coaster
{
public System.Guid CoasterId { get; set; }
public string Name { get; set; }
public Coaster()
{
CoasterId = System.Guid.NewGuid();
}
}
Based on other posts, I have tried adding attributes to the PK:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
And I have tried modifying the OnModelCreating method:
HasKey(x => x.CoasterId).Property(x => x.CoasterId).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
These haven't worked for me. What have I missed that is causing Entity Framework to ignore the GUID that I have attached to the primary key field?
You can't fix stupid, but you can fix stupid mistakes. So as I mentioned, in the OnModelCreating method, I had this line:
HasKey(x => x.CoasterId).Property(x => x.CoasterId).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
I thought it didn't work, but that was only because a bit later in the method, there was a line that overwrote change:
Property(x => x.FpoDistrictId).HasColumnName(#"FPODistrictId").HasColumnType("uniqueidentifier").IsRequired().HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
At least I figured it out...

Entity Framework Error Deleting Entity with Foreign Key Relationship

I am having a problem deleting some entities due to a foreign key relationship. I understand the following error message and have been doing everything I can think of to delete the entities without incurring this error:
The DELETE statement conflicted with the REFERENCE constraint
"FK_QuizUserAnswer_QuizWithQuestion". The conflict occurred in
database "SomeDatabase", table "dbo.QuizUserAnswer", column
'idQuizQuestion'. The statement has been terminated.
Here is an image of the two tables in question:
I am trying to delete QuizWithQuestion entities. I have made the idQuizQuestion column nullable. So, the foreign key is nullable on the QuizUserAnswer side.
In the mapping files, I have specified that the relationship is optional:
HasMany(t => t.QuizUserAnswers)
.WithOptional(t => t.QuizWithQuestion)
.HasForeignKey(t => t.idQuizQuestion);
HasOptional(t => t.QuizWithQuestion)
.WithMany(t => t.QuizUserAnswers)
.HasForeignKey(d => d.idQuizQuestion);
I have tried many, many snippets of code, so I will post the current state of the code in the hope that my intention is clear:
public void RemoveQuestionsFromQuiz(IEnumerable<int> deletedQuestions, int quizId)
{
var quiz = // code which retrieves quiz
foreach (var deletedQuestion in deletedQuestions)
{
var quizWithQuestion = quiz.QuizWithQuestions.FirstOrDefault(q => q.Id == deletedQuestion);
if (!ReferenceEquals(null, quizWithQuestion))
{
db.Entry(quizWithQuestion).State = EntityState.Deleted;
}
}
db.SaveChanges();
}
Another attempt looks like this:
public void RemoveQuestionsFromQuiz(IEnumerable<int> deletedQuestions, int quizId)
{
var quiz = // code which retrieves quiz
foreach (var deletedQuestion in deletedQuestions)
{
var quizWithQuestion = quiz.QuizWithQuestions.FirstOrDefault(q => q.Id == deletedQuestion);
if (!ReferenceEquals(null, quizWithQuestion))
{
foreach (var quizUserAnswer in quizWithQuestion.QuizUserAnswers)
{
quizUserAnswer.idQuizQuestion = null; // nullable
quizWithQuestion.QuizUserAnswers.Remove(quizUserAnswer);
db.Entry(quizUserAnswer).State = EntityState.Modified;
}
quiz.QuizWithQuestions.Remove(quizWithQuestion);
db.Entry(quizWithQuestion).State = EntityState.Deleted;
}
}
_db.SaveChanges();
}
How can I delete these darn entities (I'm so close to writing a stored procedure)?
Since you already have the question ids to delete, something like this should work:
// assuming db is your DbContext
var questions = db.QuizWithQuestions
.Where(q => deletedQuestions.Contains(q.Id))
.Include(q => q.QuizUserAnswers);
// assuming this is your DbSet
db.QuizWithQuestions.RemoveRange(questions);
db.SaveChanges();
If the QuizUserAnswer entities are loaded into the context (which is what include should do), Entity Framework should handle setting the foreign keys to null.

EF6.0 "The relationship could not be changed because one or more of the foreign-key properties is non-nullable"

If I try to delete a "child" row I always get an exception. Here is a snipset:
using (var context = new CompanyContext())
{
ItemType itemType = context.ItemTypes.FirstOrDefault(i => i.Name == "ServerType");
ItemTypeItem itemTypeItem = itemType.Items.FirstOrDefault(i => i.Name == "DatabaseServer");
itemType.Items.Remove(itemTypeItem);
context.SaveChanges(); <=== exception!
}
The following exception is thrown on the SaveChanges() method.
"The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted."
Entity Configuration
public class ItemTypeConfiguration : NamedEntityConfiguration<ItemType>
{
public ConfigurationColumn ParentIDColumn;
public ConfigurationColumn ValidationPatternColumn;
public ItemTypeConfiguration() : base()
{
ParentIDColumn = new ConfigurationColumn() { Name = "ParentID", Ordinal = base.LastOrdinalPosition + 1 };
ValidationPatternColumn = new ConfigurationColumn() { Name = "ValidationPattern", Length = 1024, Ordinal=base.LastOrdinalPosition + 2};
this.Property(t => t.ParentID)
.HasColumnName(ParentIDColumn.Name)
.HasColumnOrder(ParentIDColumn.Ordinal);
this.HasOptional(t => t.Parent).WithMany().HasForeignKey(u => u.ParentID).WillCascadeOnDelete(false);
this.Property(t => t.ValidationPattern)
.HasColumnName(ValidationPatternColumn.Name)
.HasColumnOrder(ValidationPatternColumn.Ordinal)
.HasMaxLength(ValidationPatternColumn.Length);
}
...
public class ItemTypeItemConfiguration : NamedEntityConfiguration<ItemTypeItem>
{
public ConfigurationColumn ItemTypeIDColumn;
public ItemTypeItemConfiguration() : base()
{
ItemTypeIDColumn = new ConfigurationColumn(){Name="ItemTypeID", IsRequired=true, Ordinal= base.LastOrdinalPosition+1};
this.Property(t => t.ItemTypeID)
.HasColumnName(ItemTypeIDColumn.Name)
.HasColumnOrder(ItemTypeIDColumn.Ordinal);
this.HasRequired(t => t.ItemType).WithMany(t=>t.Items).HasForeignKey(u => u.ItemTypeID).WillCascadeOnDelete(true);
}
...
I found the blog but I don't have the "DeleteObject" method.
http://blog.clicdata.com/2013/07/04/the-operation-failed-the-relationship-could-not-be-changed-because-one-or-more-of-the-foreign-key-properties-is-non-nullable/
Any ideas? Thank you.
You need to delete the ItemTypeItem.
It is not possible to just remove it from the Items list as it cannot exist by itself, because it has a non-nullable foreign key referencing ItemType (ItemTypeID).
To delete the ItemTypeItem add
context.Entry(itemTypeItem).State = EntityState.Deleted;
In the entity framework 6.0 if you remove the entity from the main context set it will work. For example to remove an investment entity you would do the following:
context.Investments.Remove(entity);
context.SaveChanges();
This is different than attempting to remove the entity from its parent/owner, as the following:
bankAccount.Investments.Remove(entity);
context.SaveChanges();
This will throw the relationship could not be changed exception listed above. Hope this helps.
In entity 6.0 there is a difference between:
context.Investments.Remove(entity);
and
context.Entry(entity).State = EntityState.Deleted;
When using the first and cascading deletes are enabled, EF will internally perform the necessary deletes of child collections.
When using the second option, EF will not handle the necessary deletes, but let you handle the rebinding/deletion of these child objects.
This issue arise because we try to delete the parent table still child table data is present. We solve the problem with help of cascade delete.
In model Create method in dbcontext class.
modelBuilder.Entity<Job>()
.HasMany<JobSportsMapping>(C => C.JobSportsMappings)
.WithRequired(C => C.Job)
.HasForeignKey(C => C.JobId).WillCascadeOnDelete(true);
modelBuilder.Entity<Sport>()
.HasMany<JobSportsMapping>(C => C.JobSportsMappings)
.WithRequired(C => C.Sport)
.HasForeignKey(C => C.SportId).WillCascadeOnDelete(true);
After that,In our API Call
var JobList = Context.Job
.Include(x => x.JobSportsMappings) .ToList();
Context.Job.RemoveRange(JobList);
Context.SaveChanges();
Cascade delete option delete the parent as well parent related child table with this simple code. Make it try in this simple way.
Remove Range which used for delete the list of records in the database Thanks
The other answers describing why the error occurs are correct, but it is actually possible to get EF to fully delete the child when .Remove is called on the parent object's collection of children, you don't need to go directly to the child entity's table in the DB context and delete it from that.
To get that to work you need to set up an Identifying Relationship, as described in this answer.

Entity Framework one to many relation code first

I am having my first steps in EF 4.1. Because I was using NHibenate, the code first approach seems to me as the best one. I have problem with good mapping of one-to-many (or many-to-one) realtionship. Let's say I have 2 entities:
class ClientModel
{
int ClientID;
string Name;
virtual IList<OrderModel> Orders;
}
class OrderModel
{
int OrderID;
string Details;
virtual ClienModel Client;
}
When I leave it like that, there is an error while generating database - keys in tables are missing. I figured out I can fix it by changing names of the keys to ID (but it's not OK with my naming convention) or by adding [Key] annotation. Even if I add this annotation, still the names of tables are wrong - just like classes names but with 's'.
So I tried to use fluent API - I made mappings. But if I set mappings just like here:
class ClientMapping
{
ClientMapping()
{
this.HasKey(e => e.ClientID).Property(e => e.ID).HasColumnName("ClientID");
this.Property(e => e.Name).HasColumnName("Name");
this.HasMany(e => e.Orders).WithOptional().Map(p => p.MapKey("OrderID")).WillCascadeOnDelete();
this.ToTable("Clients");
}
}
class OrderMapping
{
OrderMapping()
{
this.HasKey(e => e.OrderID).Property(e => e.OrderID).HasColumnName("OrderID");
this.Property(e => e.Details).HasColumnName("Details");
this.HasRequired(e => e.Client).WithMany().Map(p=>p.MapKey("Client")).WillCascadeOnDelete(false);
this.ToTable("Orders");
}
}
the relation betweene tables in database is doubled.
What is the proper way to do one-to-many relationship using code-first approach? Am I thinking in a good direction or is it a wrong approach?
EDIT
OK, I have done it in the way #Eranga showed, but there is still a problem. When I'm getting Client from database, its Orders property is null (but in database it has some Orders with Order.ClientID == Client.ClientID).
You need to map both properties participating in the relationship. You need to add ClientID column to Orders table.
class ClientMapping
{
ClientMapping()
{
this.HasKey(e => e.ClientID).Property(e => e.ID).HasColumnName("ClientID");
this.Property(e => e.Name).HasColumnName("Name");
this.HasMany(e => e.Orders).WithRequired(o => o.Client)
.Map(p => p.MapKey("ClientID")).WillCascadeOnDelete();
this.ToTable("Clients");
}
}
class OrderMapping
{
OrderMapping()
{
this.HasKey(e => e.OrderID).Property(e => e.OrderID).HasColumnName("OrderID");
this.Property(e => e.Details).HasColumnName("Details");
this.ToTable("Orders");
}
}
Configuring the relationship from one entity is sufficient.
This may help (it helped me, when i couldn't figure out how this works):
If you would have the classes like this:
class ClientModel
{
int ClientId;
string Name;
}
class OrderModel
{
int OrderId;
string Details;
int ClientId;
}
Then this would represent 2 tables in your database which "wouldn't" be connected with each other via a foreign key (they would be connected via the ClientId in the OrderModel) and you could get data like "GetAllOrdersWithSomeClientId" and "GetTheClientNameForSomeClientId" from the database. BUT problems would arise when you would delete a Client from the database. Because then there would still be some Orders which would contain a ClientId which doesn't exist in the Client table anymore and that would lead to anomalies in your database.
The virtual List<OrderModel> Orders; (in the ClientModel) and virtual ClienModel Client; (in the OrderModel) are needed to create the relation aka. the foreign key between the tables ClientModel and OrderModel.
There is one thing about which i'm still not sure about. Which is the int ClientId; in the OrderModel. I guess that it has to have the same name as the ClientId in the ClientModel so that the entity framework knows which 2 attributes the foreign key has to connect. Would be nice if someone could explain this in detail.
Also, put this into your DbContext constructor if something souldn't work:
this.Configuration.ProxyCreationEnabled = false;
this.Configuration.LazyLoadingEnabled = false;