This is a migrated thread and some comments may be shown as answers.

Optimistic Concurrency and Transactions

0 Answers 30 Views
Data Access Free Edition
This is a migrated thread and some comments may be shown as answers.
This question is locked. New answers and comments are not allowed.
Chris
Top achievements
Rank 1
Chris asked on 06 Jan 2017, 11:21 PM

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

Tags
Data Access Free Edition
Asked by
Chris
Top achievements
Rank 1
Share this question
or