Hi
I came across an
issue today with the behaviour of OpenAccessContext.FlushChanges() when using
optimistic concurrency. To explain I have created a minimal example based on
the example project from nuget.
My metadata
configuration and product class looks like this…
namespace
TelerikDataAccessTimestampFlushIssue
{
public
class
TelerikDataAccessTimestampFlushIssueMetadataSource : FluentMetadataSource
{
protected
override
IList<MappingConfiguration> PrepareMapping()
{
// Getting Started with the Fluent Mapping API
List<MappingConfiguration> configurations =
new
List<MappingConfiguration>();
MappingConfiguration<Product> productConfiguration =
new
MappingConfiguration<Product>();
productConfiguration.MapType(x =>
new
{
ID = x.ID,
Price = x.Price,
ProductName = x.ProductName,
Timestamp = x.Timestamp,
}).WithConcurencyControl(OptimisticConcurrencyControlStrategy.Backend)
.ToTable(
"Products"
);
productConfiguration.HasProperty(x => x.ID)
.IsIdentity(KeyGenerator.Autoinc);
productConfiguration.HasProperty(x => x.Timestamp)
.IsVersion()
.WithDataAccessKind(DataAccessKind.ReadWrite)
.IsNotNullable()
.HasColumnType(
"timestamp"
)
.HasPrecision(0)
.HasScale(0);
configurations.Add(productConfiguration);
return
configurations;
}
}
}
namespace
TelerikDataAccessTimestampFlushIssue
{
public
class
Product
{
public
int
ID {
get
;
set
; }
public
string
ProductName {
get
;
set
; }
public
decimal
Price {
get
;
set
; }
public
long
Timestamp {
get
;
set
; }
}
}
The following code demonstrates the issue…
namespace
TelerikDataAccessTimestampFlushIssue
{
class
Program
{
private
static
readonly
Logger _LOG = LogManager.GetCurrentClassLogger();
static
void
Main(
string
[] args)
{
_LOG.Debug(
"Start"
);
// Create the database (if required)
using
(FluentContext context =
new
FluentContext())
{
Telerik.OpenAccess.ISchemaHandler schemaHandler = context.GetSchemaHandler();
string
script =
null
;
if
(schemaHandler.DatabaseExists())
{
_LOG.Debug(
"Database exists"
);
script = schemaHandler.CreateUpdateDDLScript(
null
);
}
else
{
_LOG.Debug(
"Create database"
);
schemaHandler.CreateDatabase();
script = schemaHandler.CreateDDLScript();
}
if
(!
string
.IsNullOrEmpty(script))
{
_LOG.Debug(
"Execute DDL Script"
);
schemaHandler.ExecuteDDLScript(script);
}
}
// Create a product
int
productId;
using
(FluentContext context =
new
FluentContext())
{
Product myProduct =
new
Product()
{
Price = 100,
ProductName =
"My Product"
,
};
context.Add(myProduct);
context.SaveChanges();
productId = myProduct.ID;
_LOG.Debug(
"Created product {0}"
, productId);
}
// Try to flush changes and make another change before committing the transaction.
using
(FluentContext context =
new
FluentContext())
{
Product myProduct = context.Products.Single(p => p.ID == productId);
myProduct.Price = 101;
context.FlushChanges();
myProduct.Price = 102;
// Uncomment to workaround/fix?
//context.Refresh(RefreshMode.OverwriteChangesFromStore, myProduct);
// This line fails because the timestamp has changed in the database
// (in our transaction anyway) but still has its old value on the
// instance.
context.SaveChanges();
_LOG.Debug(
"DONE"
);
}
}
}
}
I was expecting
calls to be able to call FlushChanges() and then SaveChanges() to work in the
same way that it would if I wasn't using optimistic concurrency but that is not
the case. Flushing the change to the database advances the Timestamp but this
change is not automatically refreshed in the tracked entity.
Could you please
clarify if this is by design (perhaps for performance?) or if it is a bug? If
it is by design perhaps someone could make a note in the documentation that
Refresh() has to be called if you want to modify an entity multiple times in a
transaction.
Thanks
Chris