The EntitySpaces Community

Share and learn about the EntitySpaces Architecture.
Welcome to The EntitySpaces Community Sign in | Join | Help
in
Home Forums Photos

What is required for concurrency trapping? Exception not raised

Last post 04-17-2008, 11:04 PM by ChrisH. 10 replies.
Sort Posts: Previous Next
  •  04-15-2008, 7:07 PM 8873

    What is required for concurrency trapping? Exception not raised

    I am using ES 2007.1.1210, with SQL Server provider (connecting to SQLExpress 2005 for dev/testing) and VS 2005/VB.NET

    I have added timestamp fields to the tables and regenerated the classes in order to have concurrency exceptions thrown when editing collisions occur.

    I use a vb.net unit test based on the ES unit test to check that concurrent editing raises an exception (see below). However, although the record doesn't get overwritten the exception is not raised.

    Is something required to switch on concurrency exceptions?

    This is the table definition: 

     

    Code:
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[clients]') AND type in (N'U'))
    BEGIN
    CREATE TABLE
    [clients](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [student_id] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [first_name] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [last_name] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
    [phone] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [mobile] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
    [timestamp] [timestamp] NOT NULL,

    [created] [datetime] NOT NULL,
    [modified_by] [int] NOT NULL,
    CONSTRAINT [PK_clients] PRIMARY KEY CLUSTERED
    (
    [id] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
    IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON)
    )
    END
    GO

    Here is the unit test code I am using (based directly on the ES test):

     

    Code:
            Dim test_id As Integer

    Try
    Dim
    c As New BusinessObjects.Clients
    c.AddNew()
    c.FirstName = String.Format("FIRSTNAME {0}", 1)
    c.LastName = String.Format("LASTNAME {0}", 1)
    c.StudentId = String.Format("11")
    c.ModifiedBy = Me._support.StaffUser.Id
    c.Save()

    test_id = c.Id

    c = New BusinessObjects.Clients
    c.LoadByPrimaryKey(test_id)
    c.FirstName = "MODIFIED BY 1"

    Dim c2 As New BusinessObjects.Clients
    c2.LoadByPrimaryKey(test_id)
    c2.FirstName = "MODIFIED BY 2"

    c.Save() ' written to db
    c2.Save() ' not written to db, but no exception!

    Assert.Fail("Concurrency exception not thrown")

    Catch cex As EntitySpaces.Interfaces.esConcurrencyException
    Assert.AreEqual("Error", cex.Message.ToString.Substring(0, 5))
    Catch ex As Exception
    Assert.Fail(ex.ToString)
    Finally
    Dim
    c As New BusinessObjects.Clients
    c.LoadByPrimaryKey(test_id)
    c.MarkAsDeleted()
    c.Save()
    End Try

     

     

     

  •  04-15-2008, 7:54 PM 8874 in reply to 8873

    Re: What is required for concurrency trapping? Exception not raised

    Your code looks fine, here is our unit test that is passing just fine:

     

    Code:
    try
    {
    	// Setup
    	ComputedTest entity = new ComputedTest();
    	entity.AddNew();
    	entity.Save();
    	testId = entity.Id.Value;
    
    	// Test
    	entity = new ComputedTest();
    	entity.LoadByPrimaryKey(testId);
    	entity.str.SomeDate = "2007-01-01";
    
    	ComputedTest entity2 = new ComputedTest();
    	entity2.LoadByPrimaryKey(testId);
    	entity2.str.SomeDate = "1999-12-31";
    
    	entity.Save();
    	entity2.Save();
    	Assert.Fail("Concurrency Exception not thrown.");
    }
    catch (EntitySpaces.Interfaces.esConcurrencyException cex)
    {
    	Assert.AreEqual("Error", cex.Message.ToString().Substring(0, 5));
    }
    catch (Exception ex)
    {
    	Assert.Fail(ex.ToString());
    }
    finally
    {
    	// Cleanup
    	ComputedTest entity = new ComputedTest();
    	entity.LoadByPrimaryKey(testId);
    	entity.MarkAsDeleted();
    	entity.Save();
    }
    break;
    
     

    I wonder if naming your column "timestamp" could be causing an issue? not likely but that caught my eye. Are you using stored procedures? If so you would need to regenerate those also.


    EntitySpaces | Twitter | BLOG | Please honor our Software License
  •  04-16-2008, 1:15 AM 8875 in reply to 8873

    Re: What is required for concurrency trapping? Exception not raised

    That test can fail in one of two ways. If no exception at all is thrown in the "try" block, the test will fail with the specific message in NUnit, "Concurrency Exception not thrown." If any exception other than a concurrency exception is thrown in the "try" block, then the second "catch" block will pass on the exception to NUnit.

    Your CREATE TABLE has a datetime field [created] that is defined NOT NULL, but has no default specified. Based on that, the very first c.Save() should throw a "column does not allow nulls. INSERT fails." exception. Nothing should be saving to the database.


    David Neal Parsons
    www.entityspaces.net
  •  04-16-2008, 2:58 AM 8878 in reply to 8875

    Re: What is required for concurrency trapping? Exception not raised

    I accidently failed to post part of the table definition which sets a default getdate() value for the created field. Please be assured that the record is being saved.

    The test is failing with the 'concurrency exception not thrown' - no exception is being thrown.
     

  •  04-16-2008, 6:28 AM 8889 in reply to 8878

    Re: What is required for concurrency trapping? Exception not raised

    Do you have the following attribute set in your config file?

    databaseVersion="2005"

    If using stored procedures, did you regen with "SQL Server 2005" checked in the SQL Server SP template?


    David Neal Parsons
    www.entityspaces.net
  •  04-16-2008, 7:42 PM 8915 in reply to 8889

    Re: What is required for concurrency trapping? Exception not raised

    Yes, databaseVersion="2005" in config. The s/w is not using stored procedures.

    The timestamp change is being recognised at some level, as the second save (c2.save) does not update the record. However, even though this save fails, no exception is raised.

    I have tried tracing into the save, but quickly got lost in a maze of getmeta.. things and couldn't see anywhere where the save was failing. 

  •  04-17-2008, 4:52 AM 8922 in reply to 8915

    Re: What is required for concurrency trapping? Exception not raised

    Why don't you just do a Sql Trace in SQL Server and capture the SQL. This has always worked in ES since the very beginning and passes all unit tests. If you capture the SQL which is very easy and will only take a few minutes will know definitively what is up.

    EntitySpaces | Twitter | BLOG | Please honor our Software License
  •  04-17-2008, 5:43 PM 8931 in reply to 8922

    Re: What is required for concurrency trapping? Exception not raised

    It turned out to be an interaction with the audit trail triggers on the table (which I had forgotten about). Removing the triggers has the test passing. Now I need to work out what is happening...
  •  04-17-2008, 6:41 PM 8932 in reply to 8931

    Re: What is required for concurrency trapping? Exception not raised

    It appears that an UPDATE trigger containing an INSERT breaks the concurrency exceptions. I hope there is a work-around as otherwise I have to trade collision detection for audit trails!

    Here's the test trigger I used, with a dummy table to ensure that it wasn't related to the specific audit tables:

    Code:
    CREATE TABLE [dbo].[ttable](
    [col1] [varchar](50) NOT NULL ) ON [PRIMARY]

    GO

    CREATE TRIGGER tu_clients ON [dbo].[clients] FOR UPDATE
    NOT FOR REPLICATION
    AS
    BEGIN

    INSERT INTO
    ttable (col1) values('hello world');

    END GO

    With this trigger no exception is raised.

    Without the trigger the collision exception is raised.

    With the trigger without an INSERT statement the collision exception is raised.

    Am I missing something blindingly obvious here?
     

  •  04-17-2008, 8:32 PM 8933 in reply to 8932

    Re: What is required for concurrency trapping? Exception not raised

    Wow, good job tracking this down. I know why you're not getting the exception. The reason why is that ADO.NET throws the exception if you try to do an update or a delete and the rows effected equals zero. This indicates to ADO.NET that the update/delete didn't occur which could only indicate a concurrency exception. However, because you are doing an INSERT in the trigger the rows effected are 1, so no exception is thrown (even though the ES code correctly refused to update). I wonder if you can just do a SET NOCOUNT in the trigger to avoid this?

     


    EntitySpaces | Twitter | BLOG | Please honor our Software License
  •  04-17-2008, 11:04 PM 8934 in reply to 8933

    Re: What is required for concurrency trapping? Exception not raised

    Thanks Mike, SET NOCOUNT solved it.

    For future reference for anyone else using an UPDATE or DELETE trigger for audit trails, this toy trigger now passes the test, inserting a single record to ttable, and raising the exception on the second save:

     

    Code:
    ALTER TRIGGER [dbo].[tu_clients] ON [dbo].[clients] FOR UPDATE 
    NOT FOR REPLICATION
    AS
    BEGIN
    set nocount on;
    
    INSERT INTO ttable (col1) values('hello world');
    
    set nocount off;
    
    END
    
     
View as RSS news feed in XML