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.
Related
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();
I have read quite a few posts about this, and I can't see how my situation is different, but it must be because it still doesn't update.
Basically, my method receives a detached entity in a message. I check it's key to see if it already exists. If it does not exist I add it to the database. (this works fine) If it exists I would like to update its values.
Here is my code:
InteropObject clientObject = (InteropObject)message.ItemToAddUpdate;
bool exists = context.InteropObjects.Any(o => o.GUID == clientObject.GUID);
if (!exists)
{
context.InteropObjects.AddObject(clientObject);
}
else
{
context.Attach(clientObject);
context.ObjectStateManager.GetObjectStateEntry(clientObject).SetModified();
}
context.SaveChanges();
thanks for the help!
The problem is that in the old ObjectContext API, setting the state to Modified does not set the properties of the entity to Modified. In the DbContext API this is fixed internally by a call that does do that.
In the ObjectContext API, you can get the same effect by setting the properties of the attached entity:
context.Attach(clientObject);
var ose = context.ObjectStateManager.GetObjectStateEntry(clientObject);
// Obtain an object array containing current values
var values = new object[ose.CurrentValues.FieldCount];
ose.CurrentValues.GetValues(values);
// "Overwrite" CurrentValues by these values:
ose.CurrentValues.SetValues(values);
So you set the properties by the values they already have, but this triggers the state manager to mark the property as Modified.
Is there an easy way to verify if an entity was updated instead of keeping tracket of a flag when testing if some properties have changed on a code first entity framework entity?
bool hasChanges = false;
if (existingEvent.Gender != tournament.Gender)
{
hasChanges = true;
existingEvent.Gender = tournament.Gender;
}
I found out I can do it like this.
DatabaseFactory.Get().Entry(existingEvent).State
I'm a bit stumped. From what I've read setting the DbContext.AutoDetectChangesEnabled to false should disable change tracking requiring one to call DbContext.DetectChanges in order to identify changes to be sent to the database.
However, it is clear from my logs below that the changes are being registered by dbContexts change tracker, even with the setting set to false.
Am I missing something?
Entity Framework Version: 5.0.0.0
DbContext class
public class ProjectContext : DbContext {
public DbSet<Project> Projects {get;set;}
}
Controller class
private ProjectContext db = new ProjectContext();
public method(){
Project p = new Project("uniqueName");
db.Configuration.AutoDetectChangesEnabled = false;
db.Projects.Add(p);
DebugChangeTracker();
db.SaveChanges();
db.Projects.First().ProjectName = "a differentName!";
DebugChangeTracker();
db.SaveChanges();
}
Logging method
private void DebugChangeTracker()
{
var path = "C:\\mypath\\";
path = path + Util.GetMsSinceEpoch().ToString() + "changeTracker.log";
using (StreamWriter sw = new StreamWriter(path))
{
var changeTracker = db.ChangeTracker;
var entries = changeTracker.Entries();
foreach (var x in entries)
{
var name = x.Entity.ToString();
var state = x.State;
sw.WriteLine("");
sw.WriteLine("***Entity Name: " + name +
"is in a state of " + state);
var currentValues = x.CurrentValues;
sw.WriteLine("***CurrentValues***");
PrintPropertyValues(currentValues,sw);
if (state != EntityState.Added)
{
sw.WriteLine("***Original Values***");
PrintPropertyValues(x.OriginalValues,sw);
}
}
}
}
First log
***Entity Name: Models.Projectis in a state of Added
***CurrentValues***
ProjectId:0
ProjectName:uniqueName
Second Log
***Entity Name: Models.Projectis in a state of Modified
***CurrentValues***
ProjectId:1
ProjectName:uniqueName
***Original Values***
ProjectId:1
ProjectName:a differentName!
Setting AutoDetectChangesEnabled to false doesn't disable change tracking. (That's what the AsNoTracking() extension method would do.) It just disables the automatic call of DetectChanges that would otherwise occur in many DbContext API methods.
But DetectChanges isn't the only method that participates in change tracking. However, if you don't call it manually at the right places where it is needed the tracked entity states are incomplete or wrong leading to incorrectly saved data.
In your case the state Added in the first part of your method is expected, even with AutoDetectChangesEnabled set to false because you only call db.Projects.Add(p). (The line is missing in your code btw, but I guess it's just a copy and paste error.) Calling a method from the DbContext API tracks changes correctly and the states in the tracker will be correct if the state was correct before the call to Add.
Or in other words: Calling an API method doesn't turn a correct state into a wrong state. But: If AutoDetectChangesEnabled is false it also won't turn a wrong state into a correct state which would be the case if AutoDetectChangesEnabled is true.
However, in the second part of your method you are just changing a POCO property value. After this point the change tracker state is wrong (Unchanged) and without a call to DetectChanges (manually or - if AutoDetectChangesEnabled is true - automatically in ChangeTracker.Entries or SaveChanges) it will never be adjusted. The effect is that the changed property value is not saved to the database.
In the last section mentioning the state Unchanged I'm refering to my own test (and also to what I would expect). I don't know and can't reproduce why you have state Modified.
Sorry, if this sounds all a bit confusing. Arthur Vickers can explain it better.
I find automatic change detection and the behaviour when disabling it rather difficult to understand and to master and I usually don't touch the default (AutoDetectChangesEnabled = true) for any tracked changes that are more complex than the simplest things (like bulk adding entities in a loop, etc.).
If someone looking for AutoDetectChangesEnabled in Entity Framework Core you can find it under ChangeTracker insted of Configuration
Usage like:
context.ChangeTracker.AutoDetectChangesEnabled = false;
//Do something here
context.PriceRecords.Add(newPriceRecord);
context.ChangeTracker.AutoDetectChangesEnabled = true;
according to Entity Framework Automatic Detect Changes's Article
they said:
you may get significant performance improvements by turning it off in some cases
look at this example from that article
using (var context = new BloggingContext())
{
try
{
context.Configuration.AutoDetectChangesEnabled = false;
// Make many calls in a loop
foreach (var blog in aLotOfBlogs)
{
context.Blogs.Add(blog);
}
}
finally
{
context.Configuration.AutoDetectChangesEnabled = true;
}
}
This code avoids unnecessary calls to DetectChanges that would have occurred while calling the DbSet.Add and SaveChanges methods.
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);