How to update detached entities without providing all fields? - entity-framework

I want to update some entities without loading those into memory first. I know this should work:
var myEntity = new MyEntity
{
PkId = 123
};
myContext.Entry(myEntity).State = EntityState.Modified;
myEntity.ProcessTime = DateTime.Now;
myContext.SaveChanges();
But upon calling SaveChanges I am facing some DbEntityValidationException stating some fields are required. I worked with EF4 (ObjectContext), but this never happened. Also, it is stating only 3 required fields although there are 8+ required fields.
Tried this too before SaveChanges (no luck):
myContext.Entry(myEntity).Property(e=>e.ProcessTime).IsModified = true;
If I use _context.Configuration.ValidateOnSaveEnabled = false; then the SaveChanges does not throw exceptions, but worse; it updates the db-row with default clr values!
How can I do this?
I am using:
EF 6.1.3 (Database First)
Oracle Data Provider NET 12c Release 4
Visual Studio 2012
Windows 7 x64

Finally, I found this utterly disgusting (and wrong) way which worked:
var myEntity = new MyEntity
{
PkId = 123
};
// If you skip this, the validation error occurs again.
// If you keep it, you might be killed.
myContext.Configuration.ValidateOnSaveEnabled = false;
var dbEntry = myContext.Entry(myEntity);
// Let it think everything is just fine.
dbEntry.State = EntityState.UnChanged;
// If you have some fields already having updated value (before attaching),
// Inform it that it has been modified
// dbEntry.Property(r => r.YourProperty).IsModified = true;
// Change your field value.
// If you change a field, the state is already modified
// So, you don't need to tell it. Just change.
myEntity.ProcessTime = DateTime.Now;
// Call to save it and hope you did not forget to init any field value of any
// entity to be inserted in this context. If you did (mistakenly), it will remain
// silent and kill you later
myContext.SaveChanges();

Related

EntityEntry.OriginalValues.ToObject() is the same as EntityEntry.Entity

I simply try to update an entity with method ProjeleriGuncelle below.
When I try to access the original values of the object in an overridden SaveChangesAsync with EntityEntry.OriginalValues.ToObject(), I see that the object contains the current values instead of the old ones.
Am I missing something? I expect clonedTypedEntity to have the values before update.
public async Task<ActionResult<int>> ProjeleriGuncelle(Proje proje)
{
var projeFound = DataContext.Projeler.AsNoTracking().FirstOrDefault(p => p.EntegrasyonId == proje.EntegrasyonId);
var entry = DataContext.Entry<Proje>(projeFound);
//Database entry is updated with the proje object as expected
entry.CurrentValues.SetValues(proje);
entry.State = EntityState.Modified;
await DataContext.SaveChangesAsync();
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
ChangeTracker.DetectChanges();
var modifiedEntries = this.ChangeTracker.Entries();
foreach (var modifiedEntry in modifiedEntries)
{
//modifiedEntry.OriginalValues.ToObject() returns the object with the currrent values instead of the original values before update
if (modifiedEntry.OriginalValues.ToObject() is not IVersionable clonedTypedEntity) continue;
clonedTypedEntity.Id = 0;
clonedTypedEntity.UstSurumId = (modifiedEntry.Entity as IVersionable)?.Id;
Add(clonedTypedEntity);
}
return base.SaveChangesAsync(cancellationToken);
}
Update :
When I remove AsNoTracking() when querying for projeFound, modifiedEntry.OriginalValues.ToObject() really returns values before update. But I don't understand the behaviour, because with var entry = DataContext.Entry<Proje>(projeFound); statement, I expect that entry object is tracked, and by updating it with entry.CurrentValues.SetValues(proje); I expect to have access to original values.
For EF the original values are the values when it starts tracking, which is when entry.State = EntityState.Modified; is called. EF is oblivious of the changes that happened in the previous line of code.
You could solve it by swapping both lines:
entry.State = EntityState.Modified; // attaches and stores original values
entry.CurrentValues.SetValues(proje);
An improvement would be to attach the entry and let the change tracker figure out if the entity was really modified.
entry.State = EntityState.Unchanged; // attaches and stores original values
entry.CurrentValues.SetValues(proje);
Now SetValues only marks actually changed properties as modified and the update statement can be much slimmer, or even not happen at all.
I had the same problem in this days....
In my Dbcontext i set this property:
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
this property disable all tracking and you can not have their OriginalValue.

Having a hard time with Entity Framework detached POCO objects

I want to use EF DbContext/POCO entities in a detached manner, i.e. retrieve a hierarchy of entities from my business tier, make some changes, then send the entire hierarchy back to the business tier to persist back to the database. Each BLL call uses a different instance of the DbContext. To test this I wrote some code to simulate such an environment.
First I retrieve a Customer plus related Orders and OrderLines:-
Customer customer;
using (var context = new TestContext())
{
customer = context.Customers.Include("Orders.OrderLines").SingleOrDefault(o => o.Id == 1);
}
Next I add a new Order with two OrderLines:-
var newOrder = new Order { OrderDate = DateTime.Now, OrderDescription = "Test" };
newOrder.OrderLines.Add(new OrderLine { ProductName = "foo", Order = newOrder, OrderId = newOrder.Id });
newOrder.OrderLines.Add(new OrderLine { ProductName = "bar", Order = newOrder, OrderId = newOrder.Id });
customer.Orders.Add(newOrder);
newOrder.Customer = customer;
newOrder.CustomerId = customer.Id;
Finally I persist the changes (using a new context):-
using (var context = new TestContext())
{
context.Customers.Attach(customer);
context.SaveChanges();
}
I realise this last part is incomplete, as no doubt I'll need to change the state of the new entities before calling SaveChanges(). Do I Add or Attach the customer? Which entities states will I have to change?
Before I can get to this stage, running the above code throws an Exception:
An object with the same key already exists in the ObjectStateManager.
It seems to stem from not explicitly setting the ID of the two OrderLine entities, so both default to 0. I thought it was fine to do this as EF would handle things automatically. Am I doing something wrong?
Also, working in this "detached" manner, there seems to be an lot of work required to set up the relationships - I have to add the new order entity to the customer.Orders collection, set the new order's Customer property, and its CustomerId property. Is this the correct approach or is there a simpler way?
Would I be better off looking at self-tracking entities? I'd read somewhere that they are being deprecated, or at least being discouraged in favour of POCOs.
You basically have 2 options:
A) Optimistic.
You can proceed pretty close to the way you're proceeding now, and just attach everything as Modified and hope. The code you're looking for instead of .Attach() is:
context.Entry(customer).State = EntityState.Modified;
Definitely not intuitive. This weird looking call attaches the detached (or newly constructed by you) object, as Modified. Source: http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx
If you're unsure whether an object has been added or modified you can use the last segment's example:
context.Entry(customer).State = customer.Id == 0 ?
EntityState.Added :
EntityState.Modified;
You need to take these actions on all of the objects being added/modified, so if this object is complex and has other objects that need to be updated in the DB via FK relationships, you need to set their EntityState as well.
Depending on your scenario you can make these kinds of don't-care writes cheaper by using a different Context variation:
public class MyDb : DbContext
{
. . .
public static MyDb CheapWrites()
{
var db = new MyDb();
db.Configuration.AutoDetectChangesEnabled = false;
db.Configuration.ValidateOnSaveEnabled = false;
return db;
}
}
using(var db = MyDb.CheapWrites())
{
db.Entry(customer).State = customer.Id == 0 ?
EntityState.Added :
EntityState.Modified;
db.SaveChanges();
}
You're basically just disabling some extra calls EF makes on your behalf that you're ignoring the results of anyway.
B) Pessimistic. You can actually query the DB to verify the data hasn't changed/been added since you last picked it up, then update it if it's safe.
var existing = db.Customers.Find(customer.Id);
// Some logic here to decide whether updating is a good idea, like
// verifying selected values haven't changed, then
db.Entry(existing).CurrentValues.SetValues(customer);

How to update only modified values (EntityFramework 5.0)?

I have this entity, want to update using entityframework
EmployeeModel employee = new EmployeeModel
{
Id = 1000, //This one must
FirstName = modifiedValue,
Email = modifiedValue,
LastName = originalValue,
Phone = originalValue
};
Code to update
_db.ObjectStateManager.ChangeObjectState(employee, EntityState.Modified);
_db.SaveChanges();
This is the SQL statement got once updated
Update Employee set Id=1138,FirstName='modifiedValue',Email='modifiedValue',LastName= 'OriginalValue',phone='originalValue' where Id=1138
But I am expecting this
Update Employee set FirstName='modifiedValue', Email='modifiedValue' where Id=1138.
I dont know what I am missing here. Please let me know.
This problem is common when dealing with DTOs. An employee entity is fetched from the database, mapped to a DTO and sent over the wire. The client then modifies this DTO and sends it back to the server.
When you touch (set) a property on an EF entity, EF will assume that the value has been changed. Even if the old value and the new value are exactly the same.
The same problem occurs when you map the DTO to a new Entity and attach it to EF and updating its status to 'Modified'.
Using AutoMapper:
// This will result in the full update statement
var employee = AutoMapper.Mapper.Map<EmployeeDto, Employee>(dto);
// This will result in a smaller update statement (only actual changes)
var employee = dbContext.Employees.Find(dto.Id);
AutoMapper.Mapper.Map(dto, employee);
Or, manually (I would avoid doing this, but just for the sake of completeness):
// This will result in a smaller update statement (only actual changes)
var employee = dbContext.Employees.Find(dto.Id);
if (employee.Email != dto.Email )
employee.Email = dto.Email;
There are probably some other ways for dealing with this problem... but using AutoMapper together with Entity Framework correctly is definitely one of the easiest ways.
This is the solution I got
var entity = _db.CreateObjectSet<Employee>();
entity.Detach(employee);
entity.Attach(employee);
foreach (string modifiedPro in employeeModel.ModifiedProperties){
_db.ObjectStateManager.GetObjectStateEntry(employee).SetModifiedProperty(modifiedPro);}
_db.SaveChanges();
Only modified values in the sql update statement
Update Employee set FirstName='modifiedValue', Email='modifiedValue' where Id=1138.
If anybody knows better answer than this, Please post your suggestions
You can try this way
public update(Person model)
{
// Here model is model return from form on post
var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault();
var UpdatedObj = (Person) Entity.CheckUpdateObject(oldobj, model);
db.Entry(oldobj).CurrentValues.SetValues(UpdatedObj);
}
public static object CheckUpdateObject(object originalObj, object updateObj)
{
foreach (var property in updateObj.GetType().GetProperties())
{
if (property.GetValue(updateObj, null) == null)
{
property.SetValue(updateObj,originalObj.GetType().GetProperty(property.Name)
.GetValue(originalObj, null));
}
}
return updateObj;
}

DbContext only validate changed properties

I'm experimenting with updating properties on an entity without getting the entity first from the database.
The trouble is I only wish to update some properties and the entity validator complains that the non-nullable values have not been filled even though I'm not updating those.
Is my only option to turn of the validator?
I'd rather not turn of the validator, as I'd like to validate the properties I'm updating.
TestContext context = new TestContext();
LearningResource learningResource = new LearningResource();
learningResource.LearningResourceID = 132;
DbEntityEntry<LearningResource> entry = context.Entry(learningResource);
context.LearningResources.Attach(learningResource);
entry.State = EntityState.Unchanged;
learningResource.Title = "alex";
entry.Property(e => e.Title).IsModified = true;
//Only seems to work if I do this.
//context.Configuration.ValidateOnSaveEnabled = false;
context.SaveChanges();
That is "a feature". You must turn off global validation and validate every changed property separately.
var result = entry.Property(e => e.Title).GetValidationResult();
I also don't understand why this doesn't happen out of the box.

Entity Framework Update - Overwrite values or not

I am using Entity Framework 4.1 to perform CRUD operations against my database. I have turned off the following properties:
this.Configuration.ProxyCreationEnabled = false;
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.AutoDetectChangesEnabled = false;
My method to update a user object
public void Edit(User user)
{
_context.Entry(user).State = System.Data.EntityState.Modified;
_context.SaveChanges();
}
Ive retrieved:
User.Forename = Joe
User.Surname = Bloggs
Ive passed the user object to my edit method with
User.Forename = Joe
User.Surname = Bloggs
If I pass my user object to my Edit method but i haven't changed any of its properties, as above. Will the properties be over written in the database with the same value or will Entity Framework know the value hasn't changed?
Since you explicitly set the state to Modified, EF does send an update statement to the database even if none of the property values have changed.
If you don't want EF to update the database with the same values, you'll have to add logic to track whether the values have changed since you are setting AutoDetectChangesEnabled to false.