I am just trying to realize some database stuff with EF 4.0. I try to force a OptimisticConcurrencyException, but it does not seem to work. I assume that if the data on the database have changed immediately before I make call to context.SaveChanges(), the framework should throw an OC exception - but it does not! I had a look in update-command and voilà - the update command does not contain the properties from the entities which I have marked with concurrencyMode FIXED in the edmx properties window for that property.
Why are the changes in my database table (set some properties as FIXED for concurrency mode) not reflected in my updatecommand?
Here is everything I have already posted somewhere, but I guess all the problems come up because of the FIXED values which are not reflected in my update command?
Anybody got a clue?
USE [MyProject]
GO
/****** Object: Table [dbo].[History] Script Date: 02/19/2011 20:14:49 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[History](
[HistoryId] [bigint] IDENTITY(1,1) NOT NULL,
[HistoryKey] [varchar](100) NOT NULL,
[HistoryText] [varchar](max) NULL,
[RowVersion] [timestamp] NOT NULL,
CONSTRAINT [PK_History] PRIMARY KEY CLUSTERED
(
[HistoryId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
The ssdl states this:
<EntityType Name="History">
<Key>
<PropertyRef Name="HistoryId" />
</Key>
<Property Name="HistoryId" Type="bigint" Nullable="false" StoreGeneratedPattern="Identity" />
<Property Name="HistoryKey" Type="varchar" Nullable="false" MaxLength="100" />
<Property Name="HistoryText" Type="xml" />
<Property Name="RowVersion" Type="timestamp" Nullable="false" StoreGeneratedPattern="Computed" />
</EntityType>
the msl this:
<EntitySetMapping Name="Histories" StoreEntitySet="History" TypeName="MyModel.History">
<ScalarProperty Name="HistoryId" ColumnName="HistoryId" />
<ScalarProperty Name="HistoryKey" ColumnName="HistoryKey" />
<ScalarProperty Name="HistoryText" ColumnName="HistoryText" />
<ScalarProperty Name="RowVersion" ColumnName="RowVersion" />
</EntitySetMapping>
and the csdl this:
<EntityType Name="History">
<Key>
<PropertyRef Name="HistoryId" />
</Key>
<Property Name="HistoryId" Type="Int64" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
<Property Name="HistoryKey" Type="String" Nullable="false" MaxLength="100" Unicode="false" FixedLength="false" />
<Property Name="HistoryText" Type="String" MaxLength="Max" Unicode="true" FixedLength="false" />
<Property Name="RowVersion" Type="Binary" Nullable="false" MaxLength="8" FixedLength="true" annotation:StoreGeneratedPattern="Computed" />
</EntityType>
POCO "history" was generated with a modified ADO.NET POCO Entity Generator in VS2010
This is the source:
class Program
{
static void Main(string[] args)
{
using (var context = new MyEntities())
{
do
{
var query20 = (from p in context.Histories select p).OrderBy(p => p.HistoryId);
((ObjectQuery) query20).MergeOption = MergeOption.OverwriteChanges;
var query2 = query20.ToList();
History history = query2[0];
Console.WriteLine("found: " + history.HistoryKey + " ==> " + history.HistoryText + " ==> " +
Convert.ToBase64String(history.RowVersion));
var origRowVersion = history.RowVersion;
Console.WriteLine("Insert new key (q for exit):");
string newtext = Console.ReadLine();
if (newtext == "q")
break;
history.HistoryText = newtext;
try
{
context.DetectChanges();
var ose = context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
context.ObjectStateManager.ChangeObjectState(history, EntityState.Modified);
context.SaveChanges(SaveOptions.DetectChangesBeforeSave);
var newRowVersion = history.RowVersion;
if (newRowVersion == origRowVersion)
{
Console.WriteLine("rowversion unchanged");
}
else
{
Console.WriteLine("rowversion changed");
}
}
catch (OptimisticConcurrencyException)
{
Console.WriteLine("concurrencyexception occured!");
}
} while (true);
}
}
}
What do I do?
I have a process A that gets a record via EF
I have a process B that gets the same record via EF
Change the value of a property of this entity in process A and run until the context.SaveChanges() method (breakpoint in debugger)
Change the value of the same property in process B and call context.SaveChanges(). I could also do this directly in the database.
The bad thing is, that when I step over the context.SaveChanges() method in process A, no OC-exception is thrown, as I would expect, as the RowVersion property in the database is present and it is marked as fixed in the edmx.
The propertychanges are saved in the database.
THEN I THOUGHT I GOT AN IDEA - BUT THIS SEEMS TO BE NOT FULLY LOGICAL: WHY SHOULD I HAVE THE PROPERTY FIXED IF IT DOES NOT WORK?
I think I guess why the exception is not thrown.
This is because the value of the fixed property is not updatd and EF will compare the originalvalue and the modifiedvalue for this fixed property...and the values are the same --> no excpetion will be thrown. Seems to be correct from the EF side, but how can I get an exception thrown without requerying the value from the database and compare manually? How can I get EF to check the value of the fixed property against the value in the database (and this value has in fact changed, because I did it manually in process B (resp. in the database))?
THE UPDATE-STATEMENT with the missing fixed properties:
=============== BEGIN COMMAND ===============
update [dbo].[History]
set [HistoryText] = @0
where ([HistoryId] = @1)
select [RowVersion]
from [dbo].[History]
where @@ROWCOUNT > 0 and [HistoryId] = @1
@0 = harald
@1 = 1
=============== END COMMAND ===============
But should include also my fixed properties? Why are they not within the update statement?
SHOULD LOOK SOMETHING LIKE THAT I ASSUME:
=============== BEGIN COMMAND ===============
update [dbo].[History]
set [HistoryText] = @0
where ([HistoryId = @1) **AND ([ROWVERSION] = @2))**
select [RowVersion]
from [dbo].[History]
where @@ROWCOUNT > 0 and [HistoryId] = @1
@0 = harald
@1 = 1
**@2 = 0x000000045ef21**
=============== END COMMAND ==============
Has somebody got any idea for me? Thanks!
I use cdsl, msdl and ssdl in a separate directory (d:\webservices, which is not my assembly output directory). These three files I genereate with EDMGEN.exe. The clue is, that the csdl file generated with the EDMGEN.EXE is different to the csdl-content-section in the xml-representation of the .edmx file and the csdl file from the edmgen.exe does not contain the CONCURRENCYMODE=FIXED attribute, whereas the csdl content in the .edmx-file does. I do not know how I can generate the 3 files from my edmx-file (metadataoutput is set to "copytooutputdirectory" - but output directory only contains the edmx-file, but I want the three files to be there :-( ), but when I manually modify the csdl file form the edmgen.exe in d:\webservices, concurrency exception behavior works fine!
If somebody can propose how the get the three files recreated without edmxgen.exe...I would really appreciate this :-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With