I have code which I thought was working for Entity Framework update sql server table. I do not get an error upon stepping through the code.
var newsToUpdate = db.tblTips.Find(id);
//if(TryUpdateModel(newsToUpdate, "",))
try
{
db.SaveChanges();
}
catch (RetryLimitExceededException)
{
ModelState.AddModelError("", "unable to save changes");
}
Notice I have that if(TryUpdateModel(... line I don't recall if I had used that when records in the database table DID update.
I can see that the Model has the correct id
DB table is not updated.
What can I do to figure out WHY in C# visual studio as to it not updating the record? There are no errors.
UPDATE:
my model is tblTips , signature on my method is
public ActionResult AddNews(tblTips tips, int? groupid, int? id)
Seems that the update page is not knowing about other columns and trying to update all the columns of which it is trying to update a column to null in which in the db table that column is a "not null"
Is it "ok" ( yes yes i know i should use a viewmodel) to do the following to exclude a column from the update?
db.Entry(tips).State = EntityState.Modified;
db.Entry(tips).Property(x => x.createdby).IsModified = false;
db.SaveChanges();
I guess I should not believe everything I read on Stackoverflow , that failed above with
Attaching an entity of type 'BRM.Data.Models.tblTips' failed because
another entity of the same type already has the same primary key
value. This can happen when using the 'Attach' method or setting the
state of an entity to 'Unchanged' or 'Modified' if any entities in the
graph have conflicting key values. This may be because some entities
are new and have not yet received database-generated key values. In
this case use the 'Add' method or the 'Added' entity state to track
the graph and then set the state of non-new entities to 'Unchanged' or
'Modified' as appropriate.
As it stands, your code now says "get this thing out of the database, then save any changes I just made", but it never actually changes the thing you got out of the database.
Most likely your problem came when you commented out the call to TryUpdateModel(), which ASP.NET MVC uses to make modifications to the given object based on parameters passed into the HTTP request.
I highly recommend using a version control system to make it easier to see what you've changed at any given point in time, and undo things you changed by mistake.
If you look at the documentation for DbContext.SaveChanges, you'll see it mentions the following...
Saves all changes made in this context to the underlying database.
Since you've made no changes (in the shown code), the expected result is happening.
Also, the api public virtual int SaveChanges() suggests you can get the number of records updated from SaveChanges, e.g.
var recordsUpdated = db.SaveChanges();
In the code as you posted it, there are no changes made to any data. Entity Framework correctly determines this and does not issue any update statements.
It appears that the TryUpdateModel call is what makes changes, though I wouldn't be able to tell without looking at its code. Since this call is commented out, the changes aren't happening.
Related
I have seen other questions about this same error, but I am unable to correct the error with those suggestions in my code; I think that this is a different problem and not a duplicate.
I have an app that makes a series of rules, of which the user can set properties in the GUI. There is a table of Rules in a connected database, with the primary key on the Rule.Id. When the user saves changes to a rule, the existing rule gets "IsActive=0" to hide it, then a new database record is made with the properties from the GUI written to the database. It looks to the user as though they have edited the rule, but the database actually sees a new rule reflecting the new properties (this allows for a history to be kept), connected to the old rule by another reference field.
In the C# code for the app, the View Model for each rule contains an EF Rule object property. When the user clicks "save" I use the parameters set in the view to build the ruleViewModel.Rule for each ruleViewModel they want to save, with porperties matching the GUI. The MainViewModel contains the DbContext object called dbo, so I use the ruleViewModel.Rule to write to the mainViewModel.dbo.Entry which I save to the Entity Framework. Here are the three basic steps performed for each saveable Rule View Model:
// get the rule from the GUI and use it to make sure we are updating the right rule in EF (which is connected to the mainViewModel)
var dboItem = ruleViewModel.MainViewModel.dbo.Rules.Single(r => r.Id == ruleViewModel.Rule.Id);
// set the values in the EF item to be those we got from the GUI
ruleViewModel.MainViewModel.dbo.Entry(dboItem).CurrentValues.SetValues(ruleViewModel.Rule);
// Save the differences
ruleViewModel.MainViewModel.dbo.SaveChanges();
If the user only saves a single rule, it all works fine, but if they subsequently try to save another, or if they save more than one at once, they get the following error, which is return by the ..SetValues(..) line:
Message = "The property 'Id' is part of the object's key information and cannot be modified. "
I see from other questions on this subject that there is a feature of EF that stops you from writing the same object twice to the database with a different Id, so this error often happens within a loop. I have tried using some of the suggestions, like adding
viewModel.MainViewModel.dbo.Rules.Add(dboItem);
and
viewModel.MainViewModel.dbo.Entry(dboItem).Property(x => x.Id).IsModified = false;
before the SaveChanges() command, but that has not helped with the problem (not to mention changing the function of the code). I see that some other suggestions say that the Entry should be created within the loop, but in this case, the entries are all existing rules in the database - it seems to me (perhaps erroneously) that I cannot create them inside the save loop, since they are the objects over which the loop is built - for each entity I find, I want to save changes.
I'm really confused about what to do and tying myself increasingly in knots trying to fix the error. It's been several days now and my sanity and self-esteem is beginning to wane! Any pointers to get me working in the right direction to stop the error appearing and allow me to set the database values would be really welcome as I feel like I have hit a complete dead end! The first time around the loop, everything works perfectly.
Aside from the questionable location of the DbContext and view models containing entities, this looks like it would work as expected. I'm assuming from the MVVM tag that this is a Windows application rather than a web app. The only issue is that this assumes that the Rule entity in your ruleViewModel is detached from the DbContext. If the DbContext is still tracking that entity reference then getting the entity from the DbContext again would pass you back the same reference.
It would probably be worth testing this once in a debug session. If you add the following:
var dboItem = ruleViewModel.MainViewModel.dbo.Rules.Single(r => r.Id == ruleViewModel.Rule.Id);
bool isReferenceSame = Object.ReferenceEquals(dboItem, ruleViewModel.Rule);
Do you get an isReferenceSame value of True or False? If True, the DbContext in your main view model is still tracking the Rule entity and the whole get dboItem and SetValues isn't necessary. If False, then the ruleViewModel is detached.
If the entities are attached and being tracked then edits to the view model entities would be persisted when you call a SaveChanges on the DbContext. (No load & SetValues needed) This should apply to single or multiple entity edits.
If the entities are detached then normally the approach for updating an entity across DbContext instances would look more like:
var context = mainViewModel.dbo;
foreach( var ruleViewModel in updatedRuleViewModels)
{
// This should associate the Entity in the ruleViewModel with the DbContext and set it's tracking state to Modified.
context.Entry(ruleViewModel.Rule).State = EntityState.Modified;
}
context.SaveChanges();
There are a couple of potential issues with this approach that you should consider avoiding if possible. A DbContext should be kept relatively short lived, so seeing a reference to a DbContext within a ViewModel is a bit of a red flag. Overall I don't recommend putting entity references inside view models or passing them around outside of the scope of the DbContext they were created in. EF certainly supports it, but it requires a bit more care and attention to assess whether entities are tracked or not, and in situations like web applications, opens the domain to invalid tampering. (Trusting the entity coming in where any change is attached or copied across overwriting the data state)
Some previous code I had written used the Find() method to retrieve single entities by their primary key:
return myContext.Products.Find(id)
This worked great because I had this code tucked into a generic class, and each entity had a different field name as its primary key.
But I had to replace the code because I noticed that it was returning cached data, and I need it to return data from the database each call. Microsoft's documentation confirmed this is the behavior of Find().
So I changed my code to use SingleOrDefault or FirstOrDefault. I haven't found anything in documentation that states these methods return cached data.
Now I am executing these steps:
Save an entity via EF.
Execute an UPDATE statement in SSMS to update the recently saved
record's Description field.
Retrieve the entity into a new entity variable using SingleOrDefault
or FirstOrDefault.
The entities being returned still have the old value in the Description field.
I have run a SQL trace, and verified that the data is being queried during step 3. This baffles me - if EF is making a round trip to the database, why is it returning cached data?
I've searched online, and most answers apply to the Find() method. Furthermore, they suggest some solutions that are merely workarounds (dispose the DbContext and instantiate a new one) or solutions that won't work for me (use the AsNoTracking() method).
How can I retrieve my entities from the database and bypass the EF cache?
The behaviour you're seeing is described in Microsoft's How Queries Work article under point 3:
For each item in the result set
a. If this is a tracking query, EF checks if the data represents an entity already in the change tracker for the context instance
If so, the existing entity is returned
It's described a little better in this blog post:
It turns out that Entity Framework uses the Identity Map pattern. This means that once an entity with a given key is loaded in the context’s cache, it is never loaded again for as long as that context exists. So when we hit the database a second time to get the customers, it retrieved the updated 851 record from the database, but because customer 851 was already loaded in the context, it ignored the newer record from the database (more details).
All of this is saying that if you make a query, it checks the primary key first to see if it already has it in the cache. If so, it uses what's in the cache.
How do you avoid it? The first is to make sure you're not keeping your DbContext object alive too long. DbContext objects are only designed to be used for one unit of work. Bad things happen if you keep it around too long, like excessive memory consumption.
Do you need to retrieve data to display to the user? Create a DbContext to get the data and discard that DbContext.
Do you need to update a record? Create a new DbContext, update the record and discard that DbContext.
This is why, when you use EF Core with dependency injection in ASP.NET Core, it is created with a scoped lifetime, so any DbContext object only lives for the life of one HTTP request.
In the rare case you really do need to get fresh data for a record you already have an object for, you can use EntityEntry.Reload()/EntityEntry.ReloadAsync like this:
myContext.Entry(myProduct).Reload();
That doesn't help you if you only know the ID though.
If you really really need to reload an entity that you only have the ID for, you could do something weird like this:
private Product GetProductById(int id) {
//check if it's in the cache already
var cachedEntity = myContext.ChangeTracker.Entries<Product>()
.FirstOrDefault(p => p.Entity.Id == id);
if (cachedEntity == null) {
//not in cache - get it from the database
return myContext.Products.Find(id);
} else {
//we already have it - reload it
cachedEntity.Reload();
return cachedEntity.Entity;
}
}
But again, this should only be used in limited cases, when you've already addressed any cases of long-living DbContext object because unwanted caching isn't the only consequence.
Ok, I have the same problem and finally found the answer,
You doing everything right, that's just how EF works.
You can use .AsNoTracking() for your purposes:
return myContext.Products.AsNoTracking().Find(id)
make sure you addedusing Microsoft.EntityFrameworkCore; at the top.
It works like a magic
I'm trying to solve an issue with the optimistic concurrency control on EF 6. I currently want to catch the DBUpdateConcurrencyException and then refresh the entity. However I am currently getting this Exception:
System.InvalidOperationException: The element at index 0 in the
collection of objects to refresh has a null EntityKey property value
or is not attached to this ObjectStateManager.
Here is a simplified version of the code that shows the purpose:
using (var dbContextTransaction = dbContext.Database.BeginTransaction(System.Data.IsolationLevel.Serializable))
{
try
{
dbContext.Commit();
}
catch(DbUpdateConcurrencyException ex)
{
((IObjectContextAdapter)KnowledgebaseContext).ObjectContext.Refresh(RefreshMode.StoreWins, en);
dbContextTransaction.Rollback();
}
}
I couldn't find much on this exception on Google or SO. Any help would be appreciated.
I have been able to solve this problem by looking at this and this. The documentation on this feature is rather scarce.
So here is the scenario(we assume that there already is a TimeStamp column the value of which gets updated with each database update):
UserA reads Entity1 and starts making changes. While UserA is making her changes, userB reads Entity1, changes it and saves it to the database. Now UserA wants to save her changes but now by definition, the exact entity that she read no longer exists. The reason for this is that the existence of that entity depends on the TimeStamp column as well which is no longer the same old value. So when I was trying to refresh Entity1 as UserA knew existed, I was getting an exception and I was not able to Refresh either.
Now we'll look at two possible solutions to a concurrency problem for an existing updated entity:
Ignore UserA's Changes(store wins): This basically means that one will Refresh the entity from the database. In order to do this, one should overwrite the TimeStamp field for Entity1 in UserA's context with the new one now residing on the database and then try to refresh the information from the server. This way the right entity can be located and retrieved and populated in Entity1 overwriting local changes. Look here for another approach than this.
Overwrite changes on the database(client wins): Here, we would overwrite the TimeStamp field and then attempt the update again. By doing so, the EF would no longer detect the update as a conflict and the data on the server is overwritten. The previously referred links contain examples for this case as well.
I don't know exactly for sure why I was getting the exception when using the Refresh method. I switched to using SetValues and GetDatabaseValues and such and my problem was solved.
My table Sections (SQL Server) has ID as a primary key (int, identity) and SortIndex column (int) for sorting purposes.
The database has a trigger which sets SortIndex := ID at each INSERT. Obviously I want to change the sorting index later, by swapping the values for two rows.
I access the data using Entity Framework, all with MVC3 web application.
The problem is, Entity Framework doesn't update the value of SortIndex after I insert a new object into the table. It also caches all the data, so the following call to get all objects from this table will also give wrong SortIndex value for this object.
I tried changing StoreGeneratedPattern for this column in EDMX. This seems to be great and elegant but doesn't solve the problem.
If I set to Identity, it causes EF to properly update the value, but it becomes read only (exception thrown when trying to change). Setting it to Computed is similar, but instead of exception being thrown the values are just not written to the DB.
I can recreate the EF object every time if I need to use it after inserting an object, just by doing:
DatabaseEntities db = new DatabaseEntities()
But it seems like ugly workaround for me.
What's a solution to this problem?
Obviously something, what doesn't require me to do some action after every insert (and take a risk that it's forgotten and unnoticed) is preferred.
In short StoreGeneratedPattern means: the value is handled by the store and your application will never modify it. In such case you will get store generated value automatically after you call SaveChanges.
If you don't use StoreGeneratedPattern you will not get value and you will have to force another query execution to refresh your entity. You can for example do:
objectContext.Refresh(RefreshMode.StoreWins, yourSection);
Generally situations where you need to update values in both database through triggers and application don't play very nicely with EF (and probably also other ORM tools).
I found the answer from 'Ladislav Mrnka' being exact and marked it as accepted. Here are other workarounds, which I found while trying to find some solution. However, the solution I was looking for is in general not possible.
One of possibilities is to set StoreGeneratedPattern = Computed to let EF know, this value is calculated. And then, make a Stored Procedure to actually change the value of SortIndex. Typically it would change values in two rows (swap them), to change the sorting order. This procedure along with a trigger at INSERT gives guarantee the data stays consistent in the DB. It's not possible to create new row without proper value set in SortIndex, it's not possible to make two objects have the same value (unless stored procedure has a bug) and it's not possible to manually break the value somehow, because it's not possible to edit through EF. Looks like a great solution.
It's easily possible to have stored procedures mapped to functions in EF.
The problem is, it's now fine to enter a new row and EF properly updates data in its cache, but the cache is not updated after calling the stored procedure. Still some manual updated or refresh function is needed. Otherwise the following call to get objects sorted by SortIndex will give wrong results.
Other than that, it's possible to set MergeOption = MergeOption.OverwriteChanges for several entities, which causes EF to update data from the DB somewhat better. With this being done, it's possible to reread the object after inserting it or calling stored procedure and it will get refreshed. However, reading a collection of objects with db.Section.OrderBy(o => o.SortIndex) will still return cached results with wrong sorting order.
If anyone is interested, it's possible to make MergeOption default to something else by adding EF partial class and then partial method OnContextCreated, like here:
public partial class DatabaseEntities
{
partial void OnContextCreated()
{
Subsection.MergeOption = MergeOption.OverwriteChanges;
Section.MergeOption = MergeOption.OverwriteChanges;
Function.MergeOption = MergeOption.OverwriteChanges;
}
}
Do you know if you'll work with that column again in the same request?
I would use the context per request scenario, which usually gets you out of many problem, because a new EF context is created with every request, so you have a fresh data once per request.
With long lived context, there can grow incosistencies as you described.
Anyways the StoreGeneratedPattern setted to computed should be right. But it updates itself only when you're storing the actual entity. It's not getting updated by inserting or updating any other entity.
from http://msdn.microsoft.com/en-us/library/dd296755(v=vs.90).aspx
If you create a new entity or change an existing entity, the values of properties with StoreGeneratedPattern set to Computed are retrieved from the server when you call the SaveChanges method in your application.
If you assign a value to a property with StoreGeneratedPattern set to Computed in your application, the value will be overwritten with the server-generated value when you call the SaveChanges method.
We're using the computed value option for SQL sequenced GUID, and it's working OK.
I had a similar situation with a Sql Server Quote table with a varchar QuoteNumber column that is a non-primary unique key whose value is generated by an after-insert trigger. The trigger is used because the generated value is derived by fetching data from a foreign key table. Sql Server schema identity declarations do not allow you to pull information from other tables.
I'd like EF to treat this varchar column like an identity and do nothing to it on update and reread it after insert. EF will do so if there is a .HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity) property to a non-identity column in the code it generates to configure the Entity like so (scroll right):
public QuoteConfiguration(string schema)
{
ToTable("Quote", schema);
HasKey(x => x.ID);
Property(x => x.ID).HasColumnName(#"ID").HasColumnType("int").IsRequired().HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
Property(x => x.QuoteNumber).HasColumnName(#"Quote_Number").HasColumnType("varchar").IsOptional().IsUnicode(false).HasMaxLength(64).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
}
My EF model is code first and generated by Simon Hughes' EntityFramework Reverse POCO Generator. At first, I could not figure out how to make the generator add this property to a column that is not declared as an identity in Sql Server.
Rereading the entire Quote entity after insert did not retrieve the auto-generated QuoteNumber. Then I discovered that re-reading just the QuoteNumber column after insert defeated the entity cache. But, I felt dirty doing it.
Finally, I worked with Simon Hughes to discover how to get his EF Reverse POCO to do it for me. You just extend the UpdateColumn function in your *.tt file like so:
Settings.UpdateColumn = (Column column, Table table) =>
{
if (table.Name.Equals("Quote", StringComparison.InvariantCultureIgnoreCase)
&& column.Name.Equals("Quote_Number", StringComparison.InvariantCultureIgnoreCase))
{
column.IsStoreGenerated = true;
}
}
I'm having a small problem with ASP.NET MVC and Entity Framework 4. I have an entity called "UF" and another one called "Pais", and they have this relation:
UF [* ... 0..1] Pais
I can access the Pais object directly from UF using a navigation property:
UF.Pais.Whatever = "foobar";
Currently I have a View which inserts a new item into the database, and it has an editor for "Pais.Codigo" ("Codigo" is the primary key for Pais). So when I insert a new UF, the framework creates an instance of class UF with a reference to an instance of class Pais. Then this is done:
if (ModelState.IsValid)
{
db.UFs.AddObject(uf);
db.SaveChanges();
return View();
}
The problem is that the EF is inserting a new Pais into the database, so it basically ignores the existing one.
For example, if let's say my object UF has a Pais with an ID of 1. The current value of uf.Pais.Codigo is 1. Other attributes, like the description, is currently null. When I execute the SaveChanges, both "uf" and "uf.Pais" are with the state of Added. The correct state for "uf.Pais" should be Unchanged, since it already exists on the database.
My question is: there's some way of changing the default relationship EntityState for Unchanged? The following code solves the problem, but adding it to each function with adds a new entry to the database (and for each FK) is overkill!
db.ObjectStateManager.ChangeObjectState(uf.Pais, EntityState.Unchanged);
That's it. I'm not sure if I was clear enough. Feel free to ask more information if needed. And sorry for any english mistakes!
Thanks,
Ricardo
PS: "Pais" stands for Country and "UF" for State.
My question is: there's some way of changing the default relationship
EntityState for Unchanged?
Yes by calling Attach instead of Unchanged.
The following code solves the problem, but adding it to each function
with adds a new entry to the database (and for each FK) is overkill!
No it is not overkill, it is a solution because either Attach or AddObject will always make the operation for all entities and associations in entity graph. That means that calling AddObject will make everything what context is not aware of yet as Added and Attach will make everything what context is not aware of as Unchanged (so you will in turn have to set each modified or inserted entity to its correct state). That is how EF works if you are using detached entities.
Another solution for the problem is making the connection after the UF is added:
// Here UF.Pais is null
db.UFs.AddObject(uf);
// Create dummy Pais
var pais = new Pais { Id = "Codigo" };
// Make context aware of Pais
db.Pais.Attach(pais);
// Now make the relation
uf.Pais = pais;
db.SaveChanges();
If you are working with detached entities you are always responsible for setting the correct state for each entity (and independent association). So you will either use attached entities to let EF make the magic for you (as shown in the example) or you will use the approach you dislike. In more complex scenarios you can find out that best approach is to load entity graph again and merge incoming changes into the attached graph.