ChangeConflictException in Linq to Sql update - linq-to-sql

I'm in a world of pain with this one, and I'd very much appreciate it if someone could help out.
I have a DataContext attached to a single test table on a database. The test table is as follows:
CREATE TABLE [dbo].[LinqTests](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[StringValue] [varchar](255) NOT NULL,
[DateTimeValue] [datetime] NOT NULL,
[BooleanValue] [bit] NOT NULL,
CONSTRAINT [PK_LinqTests] PRIMARY KEY CLUSTERED ([ID] ASC))
ON [PRIMARY]
Using Linq, I can add, retrieve and delete rows from the test table, but I cannot update a row -- for an UPDATE, I always get a ChangeConflictException with an empty ObjectChangeConflict.MemberConflicts collection. Here is the code used:
var dataContext = new UniversityDataContext();
dataContext.Log = Console.Out;
for (int i = 1; i <= 1; i++) {
var linqTest = dataContext.LinqTests.Where(l => (l.ID == i)).FirstOrDefault();
if (null != linqTest) {
linqTest.StringValue += " I've been updated.";
}
else {
linqTest = new LinqTest {
BooleanValue = false,
DateTimeValue = DateTime.UtcNow,
StringValue = "I am in loop " + i + "."
};
dataContext.LinqTests.InsertOnSubmit(linqTest);
}
}
try {
dataContext.SubmitChanges(ConflictMode.ContinueOnConflict);
}
catch (ChangeConflictException exception) {
Console.WriteLine("Optimistic concurrency error.");
Console.WriteLine(exception.Message);
Console.ReadLine();
}
Console.ReadLine();
Here is the log output for an update performed through the DataContext.
UPDATE [dbo].[LinqTests]
SET [StringValue] = #p3
WHERE ([ID] = #p0) AND ([StringValue] = #p1) AND ([DateTimeValue] = #p2) AND (NOT ([BooleanValue] = 1))
-- #p0: Input BigInt (Size = 0; Prec = 0; Scale = 0) [1]
-- #p1: Input VarChar (Size = 15; Prec = 0; Scale = 0) [I am in loop 1.]
-- #p2: Input DateTime (Size = 0; Prec = 0; Scale = 0) [3/19/2009 7:54:26 PM]
-- #p3: Input VarChar (Size = 34; Prec = 0; Scale = 0) [I am in loop 1. I've been updated.]
-- Context: SqlProvider(Sql2000) Model: AttributedMetaModel Build: 3.5.30729.1
I'm running this query on a clustered SQL Server 2000 (8.0.2039). I cannot, for the life of me, figure out what's going on here. Running a similar UPDATE query against the DB seems to work fine.
Thanks in advance for any help.

I finally figured out what was happening with this. Apparently, the "no count" option was turned on for this server.
In Microsoft SQL Server Management Studio 2005:
Right click on the server and click Properties
On the left hand of the Server Properties window, select the Connections page
Under Default connection options, ensure that "no count" is not selected.
Apparently, LINQ to SQL uses ##ROWCOUNT after updates to issue an automated optimistic concurrency check. Of course, if "no count" is turned on for the entire server, ##ROWCOUNT always returns zero, and LINQ to SQL throws a ConcurrencyException after issuing updates to the database.
This isn't the only update behavior LINQ to SQL uses. LINQ to SQL doesn't perform an automated optimistic concurrency check with ##ROWCOUNT if you have a TIMESTAMP column on your table.

Is it possible that any of the data for the row has changed between when it was retrieved and the update was attempted? Because LINQ->SQL has automatic concurrency checking that will validate the contents of the object against the currently stored values (like you see in the generated query). If it is possible that any of the fields have changed for the row in the DB vs the object LINQ is tracking then the update will fail. If this is occurring and for good reason and you know what fields, you can update the object in the DBML designer; select the field at cause and change the "Update Check" property to "Never".

I had the same issue with SQL Server 2008 and the connection option no count already turned of.
Instead of changing the Update Check property to Never (as Quintin suggests), I set it to WhenChanged and the issue was solved.

First log details about the problem, what row and what field is in conflict and what values are in conflict.
To implement such detail log, see my solution here:
What can I do to resolve a "Row not found or changed" Exception in LINQ to SQL on a SQL Server Compact Edition Database?

Related

Linq to SQL concurrency problem

Hallo,
I have web service that has multiple methods that can be called. Each time one of these methods is called I am logging the call to a statistics database so we know how many times each method is called each month and the average process time.
Each time I log statistic data I first check the database to see if that method for the current month already exists, if not the row is created and added. If it already exists I update the needed columns to the database.
My problem is that sometimes when I update a row I get the "Row not found or changed" exception and yes I know it is because the row has been modified since I read it.
To solve this I have tried using the following without success:
Use using around my datacontext.
Use using around a TransactionScope.
Use a mutex, this doesn’t work because the web service is (not sure I am calling it the right think) replicated out on different PC for performance but still using the same database.
Resolve concurrency conflict in the exception, this doesn’t work because I need to get the new database value and add a value to it.
Below I have added the code used to log the statistics data. Any help would be appreciated very much.
public class StatisticsGateway : IStatisticsGateway
{
#region member variables
private StatisticsDataContext db;
#endregion
#region Singleton
[ThreadStatic]
private static IStatisticsGateway instance;
[ThreadStatic]
private static DateTime lastEntryTime = DateTime.MinValue;
public static IStatisticsGateway Instance
{
get
{
if (!lastEntryTime.Equals(OperationState.EntryTime) || instance == null)
{
instance = new StatisticsGateway();
lastEntryTime = OperationState.EntryTime;
}
return instance;
}
}
#endregion
#region constructor / initialize
private StatisticsGateway()
{
var configurationAppSettings = new System.Configuration.AppSettingsReader();
var connectionString = ((string)(configurationAppSettings.GetValue("sqlConnection1.ConnectionString", typeof(string))));
db = new StatisticsDataContext(connectionString);
}
#endregion
#region IStatisticsGateway members
public void AddStatisticRecord(StatisticRecord record)
{
using (db)
{
var existing = db.Statistics.SingleOrDefault(p => p.MethodName == record.MethodName &&
p.CountryID == record.CountryID &&
p.TokenType == record.TokenType &&
p.Year == record.Year &&
p.Month == record.Month);
if (existing == null)
{
//Add new row
this.AddNewRecord(record);
return;
}
//Update
existing.Count += record.Count;
existing.TotalTimeValue += record.TotalTimeValue;
db.SubmitChanges();
}
}
I would suggest letting SQL Server deal with the concurrency.
Here's how:
Create a stored procedure that accepts your log values (method name, month/date, and execution statistics) as arguments.
In the stored procedure, before anything else, get an application lock as described here, and here. Now you can be sure only one instance of the stored procedure will be running at once. (Disclaimer! I have not tried sp_getapplock myself. Just saying. But it seems fairly straightforward, given all the examples out there on the interwebs.)
Next, in the stored procedure, query the log table for a current-month's entry for the method to determine whether to insert or update, and then do the insert or update.
As you may know, in VS you can drag stored procedures from the Server Explorer into the DBML designer for easy access with LINQ to SQL.
If you're trying to avoid stored procedures then this solution obviously won't be for you, but it's how I'd solve it easily and quickly. Hope it helps!
If you don't want to use the stored procedure approach, a crude way of dealing with it would simply be retrying on that specific exception. E.g:
int maxRetryCount = 5;
for (int i = 0; i < maxRetryCount; i++)
{
try
{
QueryAndUpdateDB();
break;
}
catch(RowUpdateException ex)
{
if (i == maxRetryCount) throw;
}
}
I have not used the sp_getapplock, instead I have used HOLDLOCK and ROWLOCK as seen below:
CREATE PROCEDURE [dbo].[UpdateStatistics]
#MethodName as varchar(50) = null,
#CountryID as varchar(2) = null,
#TokenType as varchar(5) = null,
#Year as int,
#Month as int,
#Count bigint,
#TotalTimeValue bigint
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRAN
UPDATE dbo.[Statistics]
WITH (HOLDLOCK, ROWLOCK)
SET Count = Count + #Count
WHERE MethodName=#MethodName and CountryID=#CountryID and TokenType=#TokenType and Year=#Year and Month=#Month
IF ##ROWCOUNT=0
INSERT INTO dbo.[Statistics] (MethodName, CountryID, TokenType, TotalTimeValue, Year, Month, Count) values (#MethodName, #CountryID, #TokenType, #TotalTimeValue, #Year, #Month, #Count)
COMMIT TRAN
END
GO
I have tested it by calling my web service methods by multiple threads simultaneous and each call is logged without any problems.

Do MERGE using Linq to SQL

SQL Server 2008 Ent
ASP.NET MVC 2.0
Linq-to-SQL
I am building a gaming site, that tracks when a particular player (toon) had downed a particular monster (boss). Table looks something like:
int ToonId
int BossId
datetime LastKillTime
I use a 3d party service that gives me back latest information (toon,boss,time).
Now I want to update my database with that new information.
Brute force approach is to do line-by-line upsert. But It looks ugly (code-wise), and probably slow too.
I think better solution would be to insert new data (using temp table?) and then run MERGE statement.
Is it good idea? I know temp tables are "better-to-avoid". Should I create a permanent "temp" table just for this operation?
Or should I just read entire current set (100 rows at most), do merge and put it back from within application?
Any pointers/suggestions are always appreciated.
An ORM is the wrong tool for performing batch operations, and Linq-to-SQL is no exception. In this case I think you have picked the right solution: Store all entries in a temporary table quickly, then do the UPSERT using merge.
The fastest way to store the data to the temporary table is to use SqlBulkCopy to store all data to a table of your choice.
If you're using Linq-to-SQL, upserts aren't that ugly..
foreach (var line in linesFromService) {
var kill = db.Kills.FirstOrDefault(t=>t.ToonId==line.ToonId && t.BossId==line.BossId);
if (kill == null) {
kill = new Kills() { ToonId = line.ToonId, BossId = line.BossId };
db.Kills.InsertOnSubmit(kill);
}
kill.LastKillTime = line.LastKillTime;
}
db.SubmitChanges();
Not a work of art, but nicer than in SQL. Also, with only 100 rows, I wouldn't be too concerned about performance.
Looks like a straight-forward insert.
private ToonModel _db = new ToonModel();
Toon t = new Toon();
t.ToonId = 1;
t.BossId = 2;
t.LastKillTime = DateTime.Now();
_db.Toons.InsertOnSubmit(t);
_db.SubmitChanges();
To update without querying the records first, you can do the following. It will still hit the db once to check if record exists but will not pull the record:
var blob = new Blob { Id = "some id", Value = "some value" }; // Id is primary key (PK)
if (dbContext.Blobs.Contains(blob)) // if blob exists by PK then update
{
// This will update all columns that are not set in 'original' object. For
// this to work, Blob has to have UpdateCheck=Never for all properties except
// for primary keys. This will update the record without querying it first.
dbContext.Blobs.Attach(blob, original: new Blob { Id = blob.Id });
}
else // insert
{
dbContext.Blobs.InsertOnSubmit(blob);
}
dbContext.Blobs.SubmitChanges();
See here for an extension method for this.

Why Could Linq to Sql Submit Changes Fail for Updates Despite Data in Change Set

I'm updating a set of objects, but the update fails on a SqlException that says "Incorrect Syntax near 'Where'".
So I crack open SqlProfiler, and here is the generated SQL:
exec sp_executesql N'UPDATE [dbo].[Addresses]
SET
WHERE ([AddressID] = #p0) AND ([StreetAddress] = #p1) AND ([StreetAddress2] = #p2) AND ([City] = #p3) AND ([State] = #p4) AND ([ZipCode] = #p5) AND ([CoordinateID] = #p6) AND ([CoordinateSourceID] IS NULL) AND ([CreatedDate] = #p7) AND ([Country] = #p8) AND (NOT ([IsDeleted] = 1)) AND (NOT ([IsNonSACOGZip] = 1))',N'#p0 uniqueidentifier,#p1 varchar(15),#p2 varchar(8000),#p3 varchar(10),#p4 varchar(2),#p5 varchar(5),#p6 uniqueidentifier,#p7 datetime,#p8 varchar(2)',#p0='92550F32-D921-4B71-9622-6F1EC6123FB1',#p1='125 Main Street',#p2='',#p3='Sacramento',#p4='CA',#p5='95864',#p6='725E7939-AEE3-4EF9-A033-7507579B69DF',#p7='2010-06-15 14:07:51.0100000',#p8='US'
Sure enough, no set statement.
I also called context.GetChangeSet() and the proper values are in the updates section.
Also, I checked the .dbml file and all of the properties Update Check values are 'Always'.
I am completely baffled on this one, any help out there?
I had overriden GetHashCode to return a concatenation of a few of the fields. When I changed it to return solely the hash of the primary key, it worked.
The root cause is that the updates will fail for an object whose hash code changes during its lifecycle, so when you override GetHashCode you need to pick attributes that cannot be updated, like the primary key,

Update Exist Data Using DataRow C#

I need to update my exist data in mysql database.
I write like this code;
String _id = lbID.Text;
dsrm_usersTableAdapters.rm_usersTableAdapter _t = new dsrm_usersTableAdapters.rm_usersTableAdapter();
dsrm_users _mds = new dsrm_users();
_mds.EnforceConstraints = false;
dsrm_users.rm_usersDataTable _m = _mds.rm_users;
_t.FillBy4(_m, _id);
if(_m.Rows.Count >0 )
{
DataRow _row = _m.Rows[0];
_row.BeginEdit();
_row["username"] = txtUserName.Text;
_row.EndEdit();
_row.AcceptChanges();
_t.Update(_m);
}
But nothing change my exists data. What is the Problem?
I think the problem is that you call DataRow.AcceptChanges() before calling DbDataAdapter.Update(). AcceptChanges will set the status of the datarow to "orignal" (or "not changed" - I don't remeber now). Try to move the call to AcceptChanges to after the Update.
Update requires a valid UpdateCommand when passed DataRow collection with modified rows
Yes I move the AccesptChange() after update bu now its give this error
Update requires a valid UpdateCommand when passed DataRow collection with modified rows
But now problem is, I use MySQL and I can not Wrie UpdateCommand , VS2008 does not accept the SQL command. Automaticly delete all SQL command. I dont understand the problem. So do you now another way without using SQL command (UpdateCommand) ?

Insert using linq templates not returning the id - MySQL

I'm using the latest subsonic dll and the latest linq templates from github. The db i'm inserting into is MySQL. Id column on table is primary key auto increment.
Versions:
Subsonic.Core.dll - 3.0.0.3 - (November 18, 2009 Merged pulls from Github).
LinqTemplates - July 29, 2009.
MySQL.Data.CF.dll - 6.1.2.0.
The row is inserted but the id is returned as 0.
Example of the insert:
mysqldb db = new mysqldb.mysqldbDB();
int ID = db.Insert.Into<db.myTable>(
r => r.message,
r => r.name,
r => r.status).Values(
message,
name,
status).Execute();
Am I doing something wrong? Shouldn't the new id be returned, not zero?
Found the bug in subsonic core.
It's in Subsonic.Core.Query.Insert.cs
The Execute method does not have a condition for id's returned that are of type long.
I've rewritten the method in my local version to:
public int Execute()
{
int returner = 0;
object result = _provider.ExecuteScalar(GetCommand());
if(result != null)
{
if(result.GetType() == typeof(decimal))
returner = Convert.ToInt32(result);
else if (result.GetType() == typeof(int))
returner = Convert.ToInt32(result);
else if (result.GetType() == typeof(long))
returner = Convert.ToInt32(result);
else
returner = Convert.ToInt32(result);
}
return returner;
}
I've changed the multiple if statements to else if's and added the type comparison of long. Also I've added the final else condition which does a convert to int. Not sure if that's such a good idea but it works for me.
If someone wants to update the source great. If i find time sometime soon i'll update it myself.