How do I do an update on a pure POCO object using entity framework 4?
Lets say I change the person's first name and call the repository in this manner:
public User Update(User user)
{
//User originalUser = GetUser(user.UserId);
//Is there a way to update the values that are only changed?
context.Users.Attach(user);
context.ObjectStateManager.ChangeObjectState(user, EntityState.Modified);
return user;
}
I dont want null values to update the database to null. For eg. Suppose I have LastName as a property but when passing the object to the update function, it was null. Am I going to have to get the originalUser and then update each property accordingly?
"update each property accordingly?"
No, you can use,
context.ObjectStateManager.TryGetObjectStateEntry(newItem, out entity);
// this will gives you the entity present in db and after that I suggest to write your code to change the state and save.
Also suggest you to read this
for more info on tracking changes in POCO entities
Related
I am using a SQLite database EFCore 2.0 preview in UWP Project.
The address table is split into to different entities
Delivery address,
Invoice Address
using
modelBuilder.Entity<Project>().OwnsOne(p => p.DeliveryAddress);
which works great for setting up the database, with migrations, creates the different table in the database. With test data that I have put in manually works great at reading data from these tables. But how do I save changes to the DeliveryAddress table. Nothing is getting persisted to the database, when I save the using:
public void UpdateDeliveryAddress(Project modifiedProject)
{
using (var db = new SteelFrameCalculatorDataContext())
{
db.Entry(modifiedProject).State = EntityState.Modified;
db.SaveChanges();
}
}
Project being the parent entity
2017-06-11T23:21:10.9242463+01:00 Warning 8 Microsoft.EntityFrameworkCore.Model.Validation
The key {'ProjectId'} on entity type 'Project.DeliveryAddress->Address' contains properties in shadow state - {'ProjectId'}. To configure this warning use the DbContextOptionsBuilder.ConfigureWarnings API (event id 'CoreEventId.ModelValidationShadowKeyWarning'). ConfigureWarnings can be used when overriding the DbContext.OnConfiguring method or using AddDbContext on the application service provider.
Using the following allowed in to save updates to the database. Assume the UpdateRange(entity) sets all to modified. Not sure if this is the correct way, but it works.
using (var db = new SteelFrameCalculatorDataContext())
{
db.UpdateRange(modifiedProject);
db.SaveChanges();
}
Have you tried setting the state of the child object? Looks like you're only setting the parent Project state.
Adding this should do it:
db.Entry(modifiedProject.DeliveryAddress).State = EntityState.Modified;
db.Entry(modifiedProject).Reference(a=>a.DeliveryAddress).TargetEntry.State = EntityState.Modified;
I have an EF7 DbContext with disabled change tracking because I want to mark all changes explicitly:
var entry = context.Entry(changedEntity);
entry.Property(propertyName).IsModified = true;
This works exactly as I want it to.
However, this does not work when a reference (navigation property) has been updated.
For example, let's say my entity has two properties - ParentId and Parent where ParentId is a foreign key and Parent is the reference to the parent entity.
Calling
entry.Property("Parent").IsModified = true;
does not work and throws ModelItemNotFoundException because Parent is not a property of the entity in terms of EF (it is a navigation instead).
In EF6, this could be done as follows:
var reference = context.Entry(changedEntity).Reference("Parent");
reference.CurrentValue = reference.CurrentValue;
IN EF7, there is no such function. I can get to the INavigation object with
entry.Metadata.GetNavigation("Parent")
but cannot see any way to mark it as modified.
So, how should I do it?
Note:
I know that setting ParentId would work, but this is not suitable for me because the referenced entity does not have ID yet as it has just been created and will get its ID from database when saved. Thus, I need to set it through the reference Parent.
Edit:
The note above was true for EF6 but is no longer valid for EF7 which I was not aware of. Thus, the solution is just as described in the note and answer below.
Wow, it seems that when a new entity is attached to the DbContext, it actually gets ID (-1 in my case). So, I can set ParentId even when the target entity is new and has not been added to the database yet.
I wonder whether there are any checks that the new ID is unique (there could already be an entry with ID -1 in database).
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 an ObjectContext with an update method. The method takes a generic object as a parameter. I need to attach this object to the ObjectContext and update the database with the changes the object had. example, I create a new object that has the same key as and entity in the database but some of the fields are different. I want to attach the object to its corresponding entity in the database and have it save the changes the new object has. Here is what i have in the Update method:
public void Update(BaseObject data, entitySetName)
{
AttachTo(entitySetName, data);
Refresh(RefreshMode.ClientWins, data);
SaveChanges();
}
After the refresh, the data get overwritten by the fields from the database. Leaving out the refresh also does not update the database record. Am I missing a step?
The DetectChanges() method will update the entitystate to modified if any changes have been made.
From MSDN: "In POCO entities without change-tracking proxies, the state of the modified properties changes to Modified when the DetectChanges method is called. After the changes are saved, the object state changes to Unchanged."
context.DetectChanges();
Additionally you could just set the state to modified so your method always trys to update regardless of whether anything has changed or not with:
ObjectStateManager.ChangeObjectState(data, EntityState.Modified);
Use simply:
public void Update(BaseObject data, entitySetName)
{
AttachTo(entitySetName, data);
ObjectStateManager.ChangeObjectState(data, EntityState.Modified);
SaveChanges();
}
I have a similar problem.
I want to make two inserts in the same transactionscope. The objects are related and have a FK relationship between them, but for several reasons I do not want to connect them via the navigation property, but only by ID.
This is a simplification of what I what I want to accomplish:
Order o = new Order();
OrderDetails d = new OrderDetails();
new Repository().SaveNew(o, d);
class Repository{
void SaveNew(Order o, OrderDetails d){
using (TransactionScope transaction = new TransactionScope())
{
_context.Connection.Open();
// order
_context.Orders.ApplyChanges(o);
_context.SaveChanges();
// details
d.OrderID = o.ID;
_context.OrderDetails.ApplyChanges(d);
_context.SaveChanges(); <--- UpdateException
_context.Connection.Close();
transaction.Complete();
}
}
}
The problem is that I get an UpdateException because the FK evaluation fails. I tried to remove the FK relationship and running the exact same piece of code, and it worked fine, and both objects had the right properties set. So why does this approach fail? And how should this instead be done? Again, I do not want to attach the entites via their navigation properties.
Thank you!
I would leave the FK relationship in the database, but delete the AssociationSet and Association from the SSDL. The designer won't let you do this, you have to edit the XML manually.
I am using EF 4 btw.
Then use AddObject and SaveChanges in your SaveNew method to add the first (parent) object. Set the foreign key Property on the child and add it with AddObject and SaveChanges.
I do not have development environment running to test this, but what I think is happening is:
Assuming that the id is generated in the database. At the point when you save the order you do not know the ID.
Then the order ID of the order detail is set to the ID of the order, but the order was not reloaded from the database. I suspect that the value is 0.
When you try to save the order detail with FK of 0, you get an error.
Either save both at the same time so that EF does the work for you, or reload the order.