Link
I'm using ASP.NET with C# and trying to use linq to sql to update a data context as exhibited on the blog linked above. I created the timestamp field in the table just as stated and am using the following method:
private void updateRecord(TableName updatedRecord)
{
context db = new context();
db.TableName.Attach(updatedRecord,true);
db.SubmitChanges();
}
My question is, are you supposed to assign the timeStamp field to anything in your updatedRecord before trying to call the Attach method on your data context?
When I run this code I get the following exception: System.Data.Linq.ChangeConflictException: Row not found or changed. I update all of the fields, including the primary key of the record that I'm updating before passing the object to this update method. During debugging the TimeStamp attribute of the object shows as null. I'm not sure if it's supposed to be that way or not.
Every book and resource I have says that this is the way to do it, but none of them go into great detail about this TimeStamp attribute.
I know this is quick and easy, so if anybody knows, please let me know.
Since you say that you created the time stamp field in the table, I wonder if, in the case where this column was added later, the column properties may not be set correctly.
You may want to check the properties on your TimeStamp column in the DBML designer. Make sure that:
AutoGenerated = true
Auto-Sync = Always
Time Stamp = True
Update Check = Never
The server data type should be rowversion NOT NULL
If it is not set to be auto generated and synced always, the row version won't be returned from the insert since you haven't changed it when the insert was done. Even though this value is generated by the database the DataContext needs to know this so that it can handle it properly.
In addition, now that you have a timestamp column, UpdateCheck should be set to Never for all of the other columns.
If you have a timestamp column, then to update a record (from a vanilla object): yes, I would expect to have to assign it. Otherwise, you lose the ability to use the timestamp for optimistic concurrency checking.
The idea is you take a copy of the timestamp when you get hold of your (disconnected) object, then when you update you can use this column to verify that nobody else has edited the row.
There are two common scenarios:
1: if you are only performing a short lived operation, get the record out of the database first - make your changes to the object, and simply SumbitChanges() [all with the same data-context]. The data-context will handle concurrency for you.
2: if you are disconnecting the object (for example passing it to a client application for a while), then use something like serialization (LINQ-to-SQL objects support DataContractSerializer (optionally; you need to enable it)). So serialize the object at the server, pass it to the client - the client makes changes to their copy and passes it back. The server deserializes it and uses Attach() and SubmitChanges(). The record in memory should still have the timestamp that it had when extracted from the database, so we can perform optimistic concurrency spanning all the time the record has been disconnected.
Related
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.
I'm new to managing concurrency so apologies if this question is ill-informed.
In past projects I've implemented concurrency checking by wrapping operations in a TransactionScope - something like this:
using (var scope = new TransactionScope(TransactionScopeOption.Required, options))
{
var copiedFolder = new Folder();
using (var db = CreateContext())
{
// do stuff safely
}
scope.Complete();
return copiedFolder;
}
However I've just come across Entity Framework's approach to concurrency: http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/handling-concurrency-with-the-entity-framework-in-an-asp-net-mvc-application
And I'm wondering when it's better to use one over the other. Technically, are they the same thing? If not, how do they differ?
They are not the same thing. Concurrency as a mechanism is there to ensure that no overwrite is made when two users are accessing same entity concurrently.
I'll give you an example. Imagine a row with Id 541 that has Name set to "Alex".
Assume you have two users, User A and User B, both attempting to modify name of that row. The following scenario is what concurrency is all about:
User A reads entity from the database.
User B reads entity from the database.
User A modifies value of Name to "Alexander" and commits change to
database.
User B modifies value of Name to "Alexander B" and commits change to
database.
Changes done by user A are overwritten without user B knowing about it.
What concurrency is doing is basically ensuring that if there has been a change in value between User B read and User B change commit, it will throw DbConcurrencyException indicating that entity has been changed thus providing user B ability to cancel the saving or proceed anyway.
When you want to ensure that particular property is safe from invisible overwriting, you mark it with [ConcurrencyCheck]. Entity Framework by the default does updates by providing Id (identity) column in WHERE clause. When another column is marked with [ConcurrencyCheck], it will also add additional condition to UPDATE command which will basically in this example be WHERE NAME = 'Alex'. So since Alex is no longer value in the database (User A has changed it), it will do 0 updates which will internally result in an exception.
Technically, are they the same thing?
No, they're not. They're not even related, necessarily.
TransactionScope is used to ensure that database changes are being committed in one transaction. You typically use TS together with Entity Framework when for whatever reason you need multiple SaveChanges calls to happen within one database transaction. A typical scenario is saving an entity and setting its generated primary key in some property of another entity. (Note that one SaveChanges call is always in one transaction and usually no TS is necessary).
TS does not resolve any concurrency conflicts. When two users affect the same records, the last user who commits the transaction wins.
Concurrency resolution is about what to do when two different users try to change the same records "simultaneously". The link you quote elaborates on the most common strategy, optimistic concurrency, which is supported by EF. The most common approach in EF is to introduce a TimeStamp column in database tables which (at least in SQL server) is incremented automatically at each update of the record. The timestamp columns are also introduced in the conceptual model (= class model) and marked as [Timestamp] (data annotation) or IsConcurrencyToken (fluent mapping), so the property will be included in update and delete commands. Briefly it looks like this:
UPDATE x SET y WHERE x.TimeStamp = <value when the record was fetched>
When another user updated the record in the mean time, EF notes zero records affected and throws a DbUpdateConcurrencyException. You can deal with this exception in a number of ways.
The Timestamp attribute can only be applied to a single byte array property, whereas the ConcurrencyCheck attribute can be applied to any number of properties with any data type.
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 am using LINQ to SQL and require some help in updating a timestamp column in a table.
Car candidate = Context.Cars.Where(c => c.CarID == car.Id).SingleOrDefault();
candidate.CarName = carToUpdate.CarName;
candidate.CarDescription = carToUpdate.CarDescription;
candidate.IsActive = carToUpdate.IsActive;
candidate.IsCab = carToUpdate.IsCab;
candidate.StockTypeId = carToUpdate.StockTypeId;
Context.SubmitChanges();
If there are no changes in the properties of the car entity, the timestamp is not changed.
(Looks like LINQ to SQL is intelligent enough to not send an update to database).
Is there anyway to forefully change timestamp from LINQ to SQL??
Please help.
Thanks.
How about adding a LastUpdatedDate column and setting that to DateTime.Now , that should ensure it will always do an update and will then change your timestamp column.
Wouldn't a trigger on the SQL table be an option?
EDIT: I think this thread answers your question force LinqToSql to submit
I think the answer is don't.
If the entity's properties are the same after you modify them as they are when you pulled the entity out of the db, it hasn't actually changed.
What's the purpose of forcing a new timestamp to generate when the row was in fact not updated?
I was reading through Object States and Change-Tracking (LINQ to SQL) and it states:
You can detect Updates by observing
notifications of changes.
Notifications are provided through the
PropertyChanging event in property
setters. When LINQ to SQL is notified
of the first change to an object, it
creates a copy of the object and
considers the object a candidate for
generating an Update statement.
For objects that do not implement
INotifyPropertyChanging, LINQ to SQL
maintains a copy of the values that
objects had when they were first
materialized. When you call
SubmitChanges, LINQ to SQL compares
the current and original values to
decide whether the object has been
changed.
Would you be able to create a partial class that adds a property that calls PropertyChanging to update a the property that isn't mapped and so only exists in code?
In database, I have a LastEditTime column of type datetime. I used this to track last row update/insert time. Linq throws exception claiming that it can not insert null at that column.
Naturally, since LastEditTime column is NOT NULL, an exception is expected. My hand generated query inserts getutcdate(). How can I ask Linq to do similar?
Create a partial class of whatever table it is. In the partial class have the following:
public partial class MyTable{
partial void OnValidate(System.Data.Linq.ChangeAction action)
{
LastEditTime = DateTime.Now;
}
}
OnValidate is always called before doing a database.submitchanges()
The simplest answer would be to assign a default value for the field in the database, i.e. getdate(). Which will ensure the field is populated with the current time/date but only if it's blank.
Linq to SQL is only an access layer on top of SQL so it's fairly dumb in that respect and you'd either need to craft the Linq to SQL classes yourself or ensure that you populate the value when you create your object instance that you are adding to the DB.
If you are looking for something that updates automatically to ensure concurrency every time you update the record then take a look at the timestamp field type.
You could create a trigger in the database and assign it to the update action. I know it works, we do this all the time on our Postgres database.
I donĀ“t know if SQL Server has triggers, but you can use a stored procedure.