I have a freshly created entity (detached because not yet saved in the DB). This entity holds another entity that already exists in the db (but is detached, too). Thus, I would use em.merge(myNewEntity) to store it.
If I want to get the new created ID, I would use em.flush() afterwards. Then I invoke myNewEntity.getId(). With persist I receive an ID generted by the DB/JPA. With merge, it does not. The ID in the object remains null. Why is that?
The result of the merge operation is not the same as with the persist operation - the entity passed to merge does not become managed. Rather, a managed copy of the entity is created and returned. This is why the original new entity will not get an id. So instead of
em.merge(newEntity);
Long id = newEntity.getId();
it should be
managedEntity = em.merge(newEntity);
Long id = managedEntity.getId();
Related
I get the error "Cannot insert explicit value for identity column in table 'UserPermission' when IDENTITY_INSERT is set to OFF" trying to insert a record as follows:
dbContext.User.Add(someUser);
dbContext.SaveChanges();
That being said, the User file has the custom class UserPermission as one of its parameters, and someUser's UserPermission is not null and has a set ID parameter. Why does this happen and is it possible to avoid getting this error without having to explicitly add a UserPermissionID foreign key parameter in my User model and setting the UserPermission parameter to null?
Thanks in advance.
This issue typically happens when deserializing entities that have related entities in the object graph then attempting to add them. UserPermission is likely an existing record that in the DB is set up with an identity PK, but EF doesn't appear to recognize that in the entity definition. (I.e. set to DatabaseGenerated(DatabaseGeneratedOption.Identity). If it had been you would most likely be seeing a different problem where a completely new duplicate UserPermission was being created.
If someUser, and it's associated someUser.UserPermission are deserialized entities then you need to do a bit of work to ensure EF is aware that UserPermission is an existing row:
void AddUser(User someUser)
{
var existingPermission = _context.UserPermissions.Local
.SingleOrDefault(x => x.UserPermissionId == someUser.UserPermission.UserPermissionId);
if (existingPermission != null)
someUser.UserPermission = existingPermission;
else
_context.Attach(someUser.UserPermission);
_context.Users.Add(someUser);
_context.SaveChanges();
}
In a nutshell, when working with detached entities that a DbContext may not be tracking, we need to check the Local state for any existing tracked instance for that ID. If we find one, we substitute the detached reference for the tracked one. If we don't find one, we attach the detached one before Adding our user.
This still isn't entirely safe because it assumes that the referenced UserPermission will exist in the database. If for any reason a non-existent UserPermission is sent in (row deleted, or fake data) you will get an exception on Save.
Passing detached entity references around can seem like a simple option at first, but you need to do this for every reference within a detached entity. If you simply call Attach without first checking, it will likely work until you come across a scenario where at runtime it doesn't work because the context happens to already be tracking an instance.
Suppose a Company has Employees. Company 'Solutions' has Employee 'James'.
These entities are both saved in the DB, and their relationship is expressed through a foreign key.
At the application level, the Employee class has a Company object property, to define the relationship.
Suppose a new company 'Better Solutions' is created, which doesn't exist in the DB yet, and James now moves to this company.
How do I tell EF to handle this? Currently I:
Save the new company 'Better Solutions' (object created with a GUID ID) to the DB:
db.Companies.Add(newCompany);
Change the Company property on Jame's instance:
james.Company = newCompany;
Tell EF that a property on James's instance has changed and needs updating:
db.Employees.Attach(james);
db.Entry(james).State = System.Data.Entity.EntityState.Modified;
But when this happens, the newCompany object doesn't have its new database ID yet (even though it's been added to the database, the object still holds the GUID ID), so when saving EF tries to do this:
UPDATE [dbo].[Employee]
SET [CompanyID] = SomeGUID,
WHERE ([EmployeeID] = JamesID)
Which of course throws an exception because no CompanyID matches that GUID:
The UPDATE statement conflicted with the FOREIGN KEY constraint
In this scenario, do I need to first push the newCompany object to the DB, then retrieve it from the DB (to get the new ID), then set this retrieved object as James's Company property?
Or does EF have a cleaner way of taking care of all this?
try to do like below. Save company first then assign it to james that will update existing employee.
db.Companies.Add(newCompany);
db.SaveChanges();
james.Company = newCompany;
db.SaveChanges();
I am using Entity Framework 6.1.1.
I am deleting single record from table as following but i am not sure whether its the only way or could further rewrite it in an efficient way.
Can someone share comments?
Reason: I am asking because many solutions in earlier posts are referring to EF 4.0 and not using the latest version 6.1.1.
Guid studentId = student.Id;
StudentReportDetail stuDetails = _context.StudentReportDetail.Find(studentId);
if (stuDetails != null)
{
_context.StudentReportDetail.Remove(stuDetails);
_context.SaveChanges();
}
There are no changes about how to delete an entity between EF 4 and EF 6. To delete an entity using Entity Framework, you need to use the Remove method on DbSet. Remove works for both existing and newly added entities.
Calling Remove on an entity that has been added but not yet saved
to the database will cancel the addition of the entity. The entity is
removed from the change tracker and is no longer tracked by the
DbContext.
Calling Remove on an existing entity that is being change-tracked
will register the entity for deletion the next time SaveChanges is
called.
Deleting with loading from the database
As the example you show in your question, you need to load first the existing entity from your context to delete it. If you don't know the Id, you can execute a query as I show below to find it first:
var report= (from d in context.StudentReportDetail
where d.ReportName == "Report"
select d).Single();
context.StudentReportDetail.Remove(report);
context.SaveChanges();
Deleting without loading from the database
If you need to delete an entity, but it’s not already in memory, it’s a little inefficient to retrieve that entity from the database just to delete it. If you know the key of the entity you want to delete, you can attach a stub that represents the entity to be deleted, and then delete this stub. A stub is an instance of an entity that just has the key value assigned. The key value is all that’s required for deleting entities.
var toDelete = new StudentReportDetail {Id = 2 };
context.StudentReportDetail.Attach(toDelete);
context.StudentReportDetail.Remove(toDelete);
context.SaveChanges();
Other way could be changing the entity's state to Deleted.DbContext has methods called Entry and Entry<TEntity>, these methods get a DbEntityEntry for the given entity and provide access to the information about the entity and return a DbEntityEntry object able to perform the action on the entity. Now you can perform the delete operation on the context by just changing the entity state to EntityState.Deleted:
var toDelete = new StudentReportDetail {Id = 2 };
context.Entry(toDelete).State = EntityState.Deleted;
context.SaveChanges();
Using a 3rd party library
There is another way but is using a 3rd party library, EntityFramework Plus, there is a nugget package you can install. You can use the batch delete operation:
context.StudentReportDetail
.Where(u => u.Id== stuDetails)
.Delete();
I have three tables Job, Contact and a reference table between them named JobContact. When I delete a record from JobContact table, so record is deleted from database, but it is still present in code. I mean, when I do a select Job by key and when I'm accessing job.JobContact, so record is still there.
How can I force EF to get the current data from this table?
Edited:
I'm using EF to delete the record. Here is a code sample how I'm doing it:
Step 1: delete record from JobContact:
var jobContactRepos = RepositoryFactory.GetRepository<JobContact>();
var jobContact = jobContactRepos.SelectByKey(jobContactId);
jobContactRepos.Delete(jobContact);
jobContactRepos.Save();
Step 2: get the job record from DB after step 1 is done:
var jobRepos = RepositoryFactory.GetRepository<Job>();
var job = jobRepos.SelectByKey(id);
After Step 1, record is deleted from DB: it is OK.
After Step 2, record is still present in the job.JobContact entity: it is not OK.
RepositoryFactory creates already a new context. So I don't understant. In which place in my code should I use Refresh() method?
thanks
You can dispose your EF context and create a new one, this will force EF to get fresh data from the DB instead of using possibly cached data. Alternatively you can call Refresh() on your context using RefreshMode.StoreWins.
But the real question is why do you delete this record from the database directly and don't use EF for it? Had you used the EF context to remove the Contact entity from the Contacts navigation property collection of your Job entity, this problem shouldn't be there in the first place.
Edit:
The reference table should be represented in EF as a navigation property Contacts in your Job entities, and a navigation property Jobs in your Contact entities. Are you using an older version of EF (I am probably not familiar enough with previous versions) or have a custom repository layer that introduces this reference entity?
What is the best way to delete an object (and its child objects) using EF? I'd like to pass just the id for the object to delete, and have EF handle deleting its dependent data (foreign key'd data). Do I have to retrieve the object first based on the id and then call "DeleteObject"?
If you have cascade configured in the database, then deleting the principle should be enough.
You can do this without a query to the database to GET the thing to be deleted using Stub entities like this:
var stub = new Principal{ID = idToDelete};
ctx.AttachTo("PrincipalsEntitySetName", stub);
ctx.DeleteObject(stub);
Of course this is not the whole story if there are references or fields used for concurrency checks you will need those too.
If on the other hand you only have a cascade delete in the model (i.e. there is no cascade in the database) you will need to get ALL the dependents in memory first, and then call delete:
var stub = new Principal{ID = idToDelete};
ctx.AttachTo("PrincipalsEntitySetName", stub);
stub.Dependendents.Load();
ctx.DeleteObject(stub);
This only works because the EF issues (what it expects to be ) redundant deletes to keep the ObjectContext in sync with what it expects to happen in the database.
Hope this helps
Alex
PS I have some tips on this topic on my MSDN blog. Check out tip 33 (cascade delete) and tip 26 (stub entities)