6

I've been getting reasonably frequent error reports from my users, a typical one is:

Error Message: Row not found or changed.
Stack Trace:
at System.Data.Linq.ChangeProcessor.SubmitChanges(ConflictMode failureMode) 
at System.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode) 
at Controls_Article_ArticleViewer.LoadArticle() 
at ViewTutorial.Page_Load(Object sender, EventArgs e) 
at System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) 
at System.Web.UI.Control.LoadRecursive() 
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

(I can give more specifics if needed). The only direct LINQ code in that function LoadArticle() is:

    using (MainContext db = new MainContext())
    {
        var q = (
                    from A in db.tblArticles
                    where A.ID == this.ArticleID && A.IsDeleted == false
                    select A
                ).SingleOrDefault();
        if (q == null)
        {
            Server.Transfer("~/404.aspx");
            Context.Response.End();
        }
        else
        {
            // Cache expired for HTML generation
            if (q.HTMLLastGenerated.AddSeconds(Settings.ArticleRegenHTMLCacheSecs) < DateTime.Now)
            {
                q.Body = ArticlesCommon.Markdown(q.MarkupBody);
                q.HTMLLastGenerated = DateTime.Now;
            }

            q.Views++;
            q.LastView = DateTime.Now;
            db.SubmitChanges();

            // Set passbakcs
            this.AuthorID = q.AuthorID;
            this.Anchor = q.Anchor;
            this.SectionID = q.SectionID;
            this.Views = q.Views;
            this.DatePublished = q.Date;
            ArticleAnchor = q.Anchor;
            ArticleAuthorID = q.AuthorID;

            // Get the latest edit
            ArticleEdit LatestEdit = ArticleEditCommon.GetLatestEdit(this.ArticleID);
            // An edit exists!
            if (LatestEdit.ID != 0)
            {
                this.Description = LatestEdit.Description;
                this.ArticleTitle = LatestEdit.Title;
                ArticleBody.Text = LatestEdit.Body;
                ArticleH1.Text = ArticleTitle;
            }
            // No edits
            else
            {
                this.Description = q.Description;
                this.ArticleTitle = q.Title;
                ArticleBody.Text = q.Body;
                ArticleH1.Text = ArticleTitle;
            }

            // Get toal comments
            TotalComments = (from C in db.tblComments where C.IsDeleted == false && C.Anchor == ArticleAnchor select new { C.ID }).Count();

            // Get author details
            var qq = (from A in db.tblForumAuthors where A.Author_ID == ArticleAuthorID select new { A.Username }).Single();
            AuthorUsername = qq.Username;
        }
    }

There may be other functions in LoadArticle that reference methods that run LINQ, but I am guessing the stacktrace would come out differently, so the code above is the cause.

Any ideas as to what would cause this? A data conflict? How is this sort of error resolved usually?

Any ideas what would cause this?

Tom Gullen
  • 61,249
  • 84
  • 283
  • 456
  • Concurrent updates to the same row could trigger this. Is there any risk of that? – bzlm Jul 03 '11 at 21:32
  • @Bzlm, I suppose so yes, the `LastViewed` field could suffer from that, I haven't built anything to handle this into the site. I did increase Googles indexing rate of my site so this sort of thing is more likely I guess? – Tom Gullen Jul 03 '11 at 21:34
  • and also the `ArticlesCommon.Markdown(q.MarkupBody)` is going to be quite a slow one, that's probably the culprit, that running when someone is trying to view it, and ideas how to resolve this? – Tom Gullen Jul 04 '11 at 08:14
  • there is a mechanism in Linq-to-SQL that lets you specify which database columns are used during an operation. But if you really *do* have concurrent reads and writes to the `LastViewed` field of the same row (or other fields), you need to use the locking features in Linq-to-SQL. I'm not up to speed on the details of those, but I've had "Row not found or changed" and solved it by making sure two simultaneous updates against the same row never occurs. – bzlm Jul 04 '11 at 08:47
  • or perhaps there is no locking in Linq-to-SQL after all. :) But if you use a `System.Transaction` around your code, surely that could work? – bzlm Jul 04 '11 at 09:05

3 Answers3

7

I've found the update check mechanisms in L2S to be a burden and unhelpful. If you need the concurrency protection, then you should probably leave them in. But in my case, dispensing with checks and letting the last-one-in keep his edits was more than sufficient for me. To that end, I disabled all the update checks for all my columns, though you may have a reason to be more discriminating. To do so, use the UpdateCheck property of ColumnAttribute when defining your properties:

[Column(Storage="lastView", Name="LastView", 
 DbType="datetime", UpdateCheck=UpdateCheck.Never)]
public DateTime LastView { ... }

That being said, I don't believe there is any facility in sqlmetal/dbml to do this for you, so you'd have to generate the entity classes yourself (not a bad idea anyway -- very powerful).

Kirk Woll
  • 76,112
  • 22
  • 180
  • 195
  • 2
    I think the issue is that Linq To Sql checks your columns on update and sometimes the columns in SQL Server may be altered in your .dbml file. Here is an alternative answer that I found helpful: http://stackoverflow.com/questions/4104797/row-not-found-or-changed-linq-c-sharp-error-on-simple-statement-help-please – JoshVarty Jan 18 '12 at 02:32
2

I had the same error, in my case a trigger on the database changed the row I was trying to update. My DbContext didn't had the change the trigger made. The solution I found was to call the .Refresh() method on the DbContext:

var productToUpdate = _dbContext.Products.SingleOrDefault(p => p.ID == product.ProductID);
_dbContext.Refresh(RefreshMode.OverwriteCurrentValues, productToUpdate);

// do changes...
_dbContext.SubmitChanges();
johnnyRose
  • 7,310
  • 17
  • 40
  • 61
0

Updating and deleting only works out of the box if you have added the tables to the dbml file. If you have handcoded your entities you will have to manually handle update and delete