Deleting Entity Object with 1 to Many Association to View-Based Entity - c#

I have a Model-First entity model which contains a Customer table linked to a view that fetches customer details from a separate database. The relationship is One to Many between the Customer table and the View and I have a navigation property on both the Customer entity and the View entity.
When I try to perform a delete using context.Customers.DeleteObject(cust) and call context.SaveChanges() I get an error:
Unable to update the EntitySet 'ViewEntity' because it has a DefiningQuery and no [DeleteFunction] element exists element to support the current operation.
I have tried setting On Delete Cascade and None and both generate the same error.
EDIT: There's not much code to show, but here you go:
Customer selectedCust = (Customer)dgvCustomers.SelectedRows[0].DataBoundItem;
if (selectedCust != null)
{
if (MessageBox.Show(String.Format("Are you sure you want to delete Customer {0}?", selectedCust.CustomerID.ToString()),
"Customer Delete Confirmation", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
// TODO - Fix this
this.ReportSchedDBContext.Customers.DeleteObject(selectedCust);
this.ReportSchedDBContext.SaveChanges();
}
}

Many related posts in SO agree with #Overmachine... you are probably missing a primary key on your entity/table.
See..
because it has a DefiningQuery and no <InsertFunction> element exists in the <ModificationFunctionMapping> element
and
Unable to update the EntitySet - because it has a DefiningQuery and no <UpdateFunction> element exist
EDIT
Just saw your comment regarding your Customer table having a primary key but your view not. Not sure whats going on without more of your code, but ultimately all you need is a Customer object with the primary key(s) set on it, then you can use the following code to delete a detached entity object.
this.ReportSchedDBContext.Entry(selectedCust).State = EntityState.Deleted;
this.ReportSchedDBContext.SaveChanges();
If you are casting from another type and that is causing problems, you could also do:
var cust = new Customer { CustomerID = selectedCust.CustomerID };
this.ReportSchedDBContext.Entry(cust).State = EntityState.Deleted;
this.ReportSchedDBContext.SaveChanges();

I was able to work around this issue by creating a dummy Stored Procedure that does nothing ("SELECT 'Done'") and using the SP Function Mapping in my two Views to set the Delete function to this Stored Procedure. Quite a hack but it worked.
I'm not sure why a Delete Function is required for a View or if I'm doing something else wrong which caused the issue, but the above worked for me.

I think you should use of all foreign keys in View. when you use all foreign keys and primary keys in view you can update and delete object as cascade.

Related

EF Core duplicate keys: The instance of entity type '' cannot be tracked because another instance with the key value '' is already being tracked

I'm working on a form using EF Core in Blazor Server. I had a number of issues with entity tracking so I set all of my queries to AsNoTracking and designed my service to create a new instance of dbcontext for each query. I think this is appropriate as none of the returned values will be edited - only the form data that users enter and the id references to the queried fields, such as employee numbers, will be stored. For inserting data, I use this:
using var context = Factory.CreateDbContext();
context.SetupForm.Attach(model);
context.Entry(model).State = EntityState.Added;
await context.SaveChangesAsync();
I am attaching the data rather than adding it and then setting the form object state to added. This ensures EF Core doesn't attempt to insert the existing employee objects when it inserts the form data.
The trouble starts in a section of the form that can have as many items as the user wants. The select a couple of employees and type in relevant data. When they submit the form, they may have selected the same employee in multiple items. As these employees were selected from separate contexts, they are two separate instances with the same ID. Of course, EF Core doesn't like this and throws errors like this one:
The instance of entity type 'Principal' cannot be tracked because another instance with the key value '{EmployeeID: 1234}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
I understand why this error is occurring but I need to be able to attach multiple entities in this way. How can I work around this issue?
One thing I could do is assign the foreign keys manually but that would be rigid and require updates whenever the model changes.
just try this
using var context = Factory.CreateDbContext();
context.Set<Principal>().Add(model);
//or maybe context.Principals.Add(model);
await context.SaveChangesAsync();
This seems to do the trick! What it does is mark any entity that lacks a key as added. Otherwise, the entity is ignored entirely.
using var context = Factory.CreateDbContext();
context.ChangeTracker.TrackGraph(model, node =>
{
if (!node.Entry.IsKeySet)
{
node.Entry.State = EntityState.Added;
}
});
await context.SaveChangesAsync();
None of the items that have a key will need to be inserted. Treating them as untracked then solves any issues with duplicates and only inserts the rows that need it.
More information: https://learn.microsoft.com/en-us/ef/core/change-tracking/identity-resolution#resolve-duplicates

Microsoft Entity Framework Core: Cannot insert duplicate key in object

I am trying to update a parent entity, GuildMemberTeam, with child entities, GuildMember, Team and GuildMemberChallenge which also has a child entities, GuildMember and Challenge but am getting the following inner exception:
Inner Exception 1: SqlException: Violation of PRIMARY KEY constraint
'PK_Challenge'. Cannot insert duplicate key in object 'dbo.Challenge'.
The duplicate key value is (15ae8798-8567-457b-812a-5820cf7843e5). The
statement has been terminated.
The only new entity is the GuildMemberTeam as all the others already exist, but these are checked and recreated as follows:
public void AddChallenge(Challenge challenge)
{
if (challenge != null)
{
var id = challenge.Id == default(Guid) ? Guid.NewGuid() : challenge.Id;
Challenge = new Challenge(id, challenge.Name, challenge.Phase, challenge.Type, challenge.Star, challenge.Gear, challenge.Level, challenge.Reward);
}
}
This works for all the other entities apart from Challenge where i get the error. Can anyone please help me understand what i am doing wrong.
It doesn't change the fact that the problem is that you are trying to insert the same row twice (same Guid=Id) into the dbo.Challenge table.
This might be due to a debugging issue or something. You can either delete the row from the table with a
DELETE FROM [Challenge] WHERE Id = '15ae8798-8567-457b-812a-5820cf7843e5' and try running the app again.
If this doesn't solve your problem your entity management is faulty and you have to revise the ID handling. Implement ID checking before you try to save your context or something like that.
The other issue might be that your classes are not defined properly and EF doesn't recognize the relations. The relationships you are talking about are not parent-child, they are either one-to-many, many-to-many, many-to-one or none. DB RELATIONS
Each of your POCO-s should contain and instance of the other class, thus you define a relationship. E.g. if your GuildMemberChallenge contains an IEnumerable and a property with type of challenge.
If none of the above are a solution I need some more code (your classes, the repository) to figure it out.
Update:
When you are adding a new GuildMemberChallenge, which I assume you are trying to do now. You should set it's Challenge property to an existing entity if it exists, if it doesn't you can create one, but at the moment you are trying to create a Challenge that already exists in the database.
You are creating new Challenge but pass id of existing Challenge if it is set.
var id = challenge.Id == default(Guid) ? Guid.NewGuid() : challenge.Id;
I think you, that if you create new entity you should always create new Id
var id = Guid.NewGuid();

Delete object from an entity with many to many relationship

I am new to Entity Framework so I need help with deleting an object from an entity.
I have 2 tables which are in many to many relationship and an association table connecting them in the database. In the model there are only two tables and the association one is presented by navigation properties as this is how the EF works. Now I need to delete an object from the first table by context.EntityName.DeleteObject(object) but when I try to do it the code fails with error "The DELETE statement conflicted with the REFERENCE constraint FK..", which is a foreign key from the association table to the entity, which object I try to delete. I wonder how to fix this. Could you please help me?
Here is how the tables look like:
Teachers
Teacher_ID
FirstName
LastName
TimetableDetail
TimetableDetail_ID
EducationalDiscipline_ID
Weekday
StartTime
Duration
and the associaion table:
TimetableDetailTeachers
Teacher_ID
TimetableDetail_ID
And here is how I try to delete it:
TimetablesEntities context = new TimetablesEntities();
TimetableDetail detail = context.TimetableDetails.SingleOrDefault(td => td.TimetableDetail_ID == timetableDetailId);
context.TimetableDetails.DeleteObject(detail);
context.SaveChanges();
Thanks in advance!
You just need to clear the association table by clearing the Teachers list for a particular TimetableDetail. Using your code...
TimetablesEntities context = new TimetablesEntities();
TimetableDetail detail = context.TimetableDetails.SingleOrDefault(td => td.TimetableDetail_ID == timetableDetailId);
detail.Teachers.Clear();
context.TimetableDetails.DeleteObject(detail);
context.SaveChanges();
The key line being detail.Teachers.Clear()
Yeah, this is a tricky one.
What you need to do is clear the entities out of the EF's underlying local storage. The example shows what to do when you clear all details for a specific teacher (or even only some details) and then save that teacher's entity. With that in mind, here is some example repository code:
public void EditTeacher(Teacher teacher)
{
if (teacher == null)
{
throw new ArgumentNullException("teacher");
}
YourDbContext.Entry(teacher).State = EntityState.Modified;
// Remove all timetable details that have an orphaned relationship.
// (E.g., orphaning occurs when 'teacher.TimetableDetails.Clear()'
// is called or when you delete one particular TimetableDetail
// entity for a teacher)
YourDbContext.TimetableDetails
.Local
.Where(td => td.Teacher == null)
.ToList()
.ForEach(td => YourDbContext.TimetableDetails.Remove(td));
YourDbContext.SaveChanges();
}
I hope this helps.
For further reading, take a look at: Deleting orphans with Entity Framework.
Edit:
The example code above assumes that elsewhere in your code you have defined and created YourDbContext as a member variable within your repository class. I just wanted to point that out in case it wasn't clear.
You could do:
add row before remove detail:
context.Teachers.RemoveRange(detail.Teachers);
The key line being that...

Entity Framework 5 The relationship could not be changed because one or more of the foreign-key properties is non-nullable

I receive the fore mentioned error when trying to delete an entity.
This entity has a foreign key to a list table, but I can delete the DB entry without a problem from Heidi MySql client.
I'm trying to clear the child entities, but when i call SaveChanges on the context, it throws the mentioned error.
nquote_orderheaders header = portalDb.nquote_orderheaders.Single(f => f.QuoteOrderNumber == id);
header.nquote_orderlines.Clear();
portalDb.SaveChanges();
portalDb.nquote_orderheaders.Remove(header);
portalDb.SaveChanges();
Using .Clear() on a navigation property doesn't remove them from the database, it only clears your collection in your code. You need to iterate over your orderlines to remove them one by one.
Another possibility is to enable cascading delete feature, which allows child entities removal if parent entity is removed.

Cascading delete in Entity Framework

So I have two tables, Invoices and InvoiceItems. When I delete an Invoice, I'd like all the related InvoiceItems to be deleted as well.
I updated the relationship in SQL Server to do a cascading delete when I delete an Invoice. Entity Framework didn't recognize that change, however, but I've read that I need to manually update my EDMX to do the cascading delete.
Well in the design view of my EDMX, I clicked on the relationship between the two tables, and checked the properties to try and set my cascading delete
As you can see, there are two OnDelete properties: End1 OnDelete and End2 OnDelete
Which one do I need to set to Cascade?
I was initially confused about this as well, and the reason was that I wasn't sure of what whether it was the field Navigation Property or the field Role Name that described what object an end represented, making an answer as Devidigitals ineffective.
And the answer to that is that a end is described by the field role name.
So if we know that we want to delete all InvoiceItems for a Invoice when the invoice in question gets deleted, we probably know that we should set the OnDelete for the Invoice, and to find the specific end that represents the invoice in the relation, search for the end where the role name is "Invoice". In the above example, that is End1.
This might be obvious if you know it, but not if you don't.
If End1 is the principal of the relationship (I.e your invoice which has invoice items) then it makes sense for it to cascade deletes.

Categories