I am trying to boost my EF operation performance, and I have found some recommendations regarding DbContext.Configuration.AutoDetectChangesEnabled property
a) in some cases to turn off AutoDetectChangesEnabled - this is clear - I am using it for import functions
b) but then I have noticed this type of approach with enabling this property only before calling SaveChanges():
DbContext db = new DbContext();
db.Configuration.AutoDetectChangesEnabled = false;
//...some changes to POCO properties
db.Configuration.AutoDetectChangesEnabled = true;
db.SaveChanges();
all the changes seem to be correctly saved to database and it works noticably faster compared to approach where AutoDetectChangesEnabled property is leaved intact.
Question
So I wonder is there a reason to leave AutoDetectChangesEnabled intact? What risks there could be if I disable this property by default and then reenable each time before calling DbContext.SaveChanges?
Related post
This post suggests that there might be reasons to leave AutoDetectChangesEnabled==true, but no clear evidence when and why should do so (ok, it says - do so, if entities are EDITED). Has anybody found out argument for/against this?
EF Code First: is good to call DetectChanges just before SaveChanges?
If you look at the documentation Entity Framework Automatic Detect Changes it says:
When using most POCO entities the determination of how an entity has
changed (and therefore which updates need to be sent to the database)
is handled by the Detect Changes algorithm. Detect Changes works by
detecting the differences between the current property values of the
entity and the original property values that are stored in a snapshot
when the entity was queried or attached.
So I think this support the link that you found that if the entity is Edited, with AutoDetectChangesEnabled=true, the change is tracked.
I've been disabling the AutoDetectChangesEnabled for Insert, with great performance improvement.
Related
When editing a form, the user may sometimes not change the form and still click the submit button. In one of the controller methods below, will the save() method perform a query to the database and update the fields even if the user didn't change anything?
PostMapping("/edit_entry/{entryId}")
public String update_entry(
#PathVariable("entryId") Long entryId,
#RequestParam String title,
#RequestParam String text
) {
Entry entry = this.entryRepo.findById(entryId).get();
if (!entry.getTitle().equals(title))
entry.setTitle(title);
if (!entry.getText().equals(text))
entry.setText(text);
this.entryRepo.save(entry);
return "redirect:/entries";
}
And also, are the "if" statements necessary in this case?
What exactly happens during a call to save(…) depends on the underling persistence technology. Fundamentally there a re two categories of implementations:
Implementations that actively manage entities. Examples of this are JPA and Neo4j. Those implementations keep track of the entities returned from the store and thus are able to detect changes in the first place. You pay for this with additional complexity as the entities are usually instrumented in some way and the change detection of course also takes time even if it ends up not detecting any changes. On the upside though the only trigger updates if needed.
Implementations that do not actively manage entities. Examples are JDBC and MongoDB. Those implementations do not keep track of entities loaded from the data store and thus do not instrument them. That also means that there is no way of detecting changes as all the implementation sees is an entity instance without any further context.
In your concrete example, a MongoDB implementation would still issue an update while JPA will not issue an update at all if the request params do not contain differing values.
This question may have already been asked, sorry
I'm looking at the architecture for validating our model. Our simple validation can be achieved by using the property validation attributes (some custom) and using
ModelState.IsValid
however the problem is when validation requires access to the database or access to another property. A perfect example is to check for duplicate names. In this case we need to check the database for duplicate names where the id is not equal to that of the current object (for updates)
If we were to write this as an validation attribute to be applied to the name property this would cause to problems. Ome how do we get access to the database and two how would we get access to the id property.
So in conclusion. Is there any examples of good ways to architect a fix to this problem?
I spent some time exploring this today for a project I was working on and came to these conclusions.
It is not to bad to solve the how, much of it involves some reflection and using the validation context to inspect and access other properties of your model or using IValidationObject. The real question becomes is it okay to do validation that requires database interaction.
For one I was concerned about performance, in one particular case a validation made a query that returned an object to ensure it existed which I later needed for relationship assignment which would then cause another query.
Secondly you need to think about database concurrency. The best way to do duplication checks is during insert not before because the database could change between the two operations. This also relates to the first reason, an object could be deleted immediately after a database reported it exists.
In my particular project I felt it better to keep this sort of behavior with modifying my EF context and adding anything that went wrong to the ModelState.
If an entities properties have changed in a controller action and Update/SaveChanges is not called, will that entity be updated automatically?
I am using EF 4.1 with a repository pattern. I handle all CRUD operations in my EfRepository class like this:
public void Update(T entity)
{
try{_context.SaveChanges();}
catch{//do stuff}
}
//etc.
In my application I have an ajax/ActionResult call that gets an entity from the db, performs some basic calculations, and returns a JSON object. To make a long story short I discovered a bug in a static helper I was using in my ActionResult that was changing a property value on my entity and not the JSON-model I was returning.
My action method has no Update methods yet my entity was being updated every time I called this action. Using Sql Profiler I confirmed an update statement that was tailored to update my entity and the one(1) property my buggy static method was changing.
I placed a break point at my Update method in my repository class and even though my entity was being updated by EF, MY Update method was never called. The summary for the interface method 'Find' (which is what I use) in IDbSet says,
Finds an entity with the given primary key values. If an entity with
the given primary key values exists in the context, then it is
returned immediately without making a request to the store. Otherwise,
a request is made to the store for an entity with the given primary
key values and this entity, if found, is attached to the context and
returned. If no entity is found in the context or the store, then null
is returned.
There are some real good example here as well.
I think given my entity was attached, the short answer to my question is 'YES'. But being somewhat new to EF I found this to be a very difficult bug to figure out as it still appears there some things going on under the covers that I could not track down. I hesitated in posting this as a question but thought someone more knowledgeable could expand on my assumed answer of YES and at the bare minimum help someone else who runs across this.
If an entities properties have changed in a controller action and
Update/SaveChanges is not called, will that entity be updated
automatically?
No, ef will only propagate your changes to the database on a SaveChanges call. Save only happens manually (IE you have to explicitly call SaveChanges).
What is really important to understand though is that SaveChanges saves all current modifications to the context. This means if you are sharing a context your changes will be saved when anyone (not just you) calls SaveChanges.
I'm working with ASP.NET MVC3 using EF and Code First.
I'm writing a simple issue tracker for practice. In my Controller I have a fairly standard bit of code:
[HttpPost]
public ActionResult Edit(Issue issue) {
if (ModelState.IsValid) {
dbContext.Entry(issue).State = EntityState.Modified
.....
}
}
Question part 1
I'm trying to get my head around how the dbcontext works -
Before I've set the State on the dbContext.Entry(issue), I assume my issue object is detached. Once I set the state to be modified, the object is attached - but to what? the dbContext or the database? I'm kind of missing what this (attaching) actually means?
Question part 2
For argument's sake, let's say I decide to set the "Accepted" field on my issue. Accepted is a boolean. I start with it being false, I'm setting it to true in the form and submitting. At the point that my object is attached, what is the point of the OriginalValues collection? for example if I set a breakpoint just after setting EntityState.Modified but before I call SaveChanges() I can query
db.Entry(issue).OriginalValues["Accepted"]
and this will give me the same value as simply querying the issue object that has been passed in to the Edit....i.e. it is giving the same result as
issue.Accepted
I'm clearly missing something because the documentation says
"The original values are usually the entity's property values as they were when last queried from the database."
But this is not the case because the database is still reporting Accepted as being false (yeah, I noted the word "usually" in the docs but my code is all pretty much standard generated by MS code so....).
So, what am I missing? what is actually going on here?
The context can work only with attached entities. The attaching means that context know about the entity, it can persists its data and in some cases it can provide advanced features like change tracking or lazy loading.
By default all entities loaded from the database by the context instance are attached to that instance. In case of web applications and other disconnected scenarios you have a new context instance for every processed HTTP request (if you don't you are doing a big mistake). Also your entity created by model binder in HTTP POST is not loaded by that context - it is detached. If you want to persist that entity you must attach it and inform context about changes you did. Setting state to Entry to Modified will do both operations - it will attach entity to the context and set its global state to Modified which means that all scalar and complex properties will be updated when you call SaveChanges.
So by setting state to Modified you have attached the entity to the context but until you call SaveChanges it will not affect your database.
OriginalValues are mostly useful in fully attached scenarios where you load entity from the database and make changes to that attached entity. In such case OriginalValues show property values loaded from database and CurrentValues show actual values set by your application. In your scenario context doesn't know original values. It thinks that original values are those used when you attached the entity.
I've asked the question a few different times in a few different ways and I haven't yet gotten any responses. I'm trying again because I feel like my solution is too convoluted and I must be missing something simpler to do.
Using EF 4.1, POCO, DbContext API, AutoMapper, and Razor in an MVC 3 application.
I have a many-to-many relationship between two of my entities: Proposals and CategoryTags. I can successfully map (Automapper) a Proposal to my ProposalViewModel including the collection of CategoryTags.
In my View, I use javascript to allow the user to add, update, and remove tags by dynamically creating elements, each one that stores the ID of the chosen tag.
I can successfully post my ViewModel back to my controller with it's CategoryTags collection populated (although only with the ID property for each CategoryTag).
When that ViewModel is posted back to my controller, I don't know how to get those tags from the ViewModel and add them to my Model in such a way that db.SaveChanges() updates the database properly.
The only way I've had any success is to disconnect the CategoryTags collection in mapping (by namig them differently), iterate through each tag and manually look it up in my context and then call the .add() method. This is sloppy for a number of reasons which leads me to believe I'm doing it wrong.
Can anyone offer any direction at all?
UPDATE:
For anyone who is interested, my functional code:
Dim p As New Proposal
Dim tempTag As CategoryTag
p = AutoMapper.Mapper.Map(Of ProposalViewModel, Proposal)(pvm)
db.Proposals.Attach(p)
db.Entry(p).Collection("CategoryTags").Load()
For Each ct In pvm.Tags
tempTag = db.CategoryTags.Find(ct.Id)
If tempTag Is Nothing Then
Continue For
End If
If ct.Tag = "removeMe" Then
p.CategoryTags.Remove(tempTag)
Continue For
End If
p.CategoryTags.Add(tempTag)
Next
db.Entry(p).State = EntityState.Modified
db.SaveChanges()
Return RedirectToAction("Index")
The only working way is doing this manually - you can read full description of the problem if you want. The description is related to ObjectContext API but DbContext API is just wrapper suffering same issues (actually DbContext API suffers even more issues in this scenario and because of that I will skip solution with manually setting relationships).
In short. Once you post your data back to the controller you must create new context instance and attach your Proposal and realated CategoryTags. But after that you must inform the context about changes you did. It means you must say context which tags have been added to proposal and which have been removed. Otherwise context cannot process your changes because it doesn't do any automatic merge with data in database.
The easiest way to solve this is loading current Proposal with related CategoryTags from database (= you will have attached instances) and merge incoming data into attached object graph. It means you will manually remove and add tags based on posted values.