namespace DoTests
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Text;

    using NUnit.Framework;

    using Xtensive.Storage;
    using Xtensive.Storage.Configuration;

    [TestFixture]
    public class VersionTest
    {
        private Domain domain;

        [TestFixtureSetUp]
        public void Setup()
        {
            var cfg = new DomainConfiguration("sqlserver://localhost/DO40-Tests")
            {
                UpgradeMode = DomainUpgradeMode.Recreate
            };

            cfg.Sessions.Add(new SessionConfiguration("Default")
            {
                BatchSize = 25,
                DefaultIsolationLevel = System.Transactions.IsolationLevel.ReadCommitted,
                CacheSize = 1000,
                Options = SessionOptions.AutoShortenTransactions
            });

            cfg.Types.Register(Assembly.GetExecutingAssembly());

            domain = Domain.Build(cfg);
        }

        /// <summary>
        /// Тест механизма оптимистического параллелизма
        /// </summary>
        [Test]
        public void Test()
        {
            using (var s = Session.Open(domain))
            {
                var id = Guid.NewGuid();
                Person person;
                using (var tr = Transaction.Open())
                {
                    person = new Person(id) { Name = id.ToString() };
                    tr.Complete();
                }

                var item = Query.Single<Person>(id);

                // Version is ok
                var values = new Dictionary<string, object>() { { "Name", "new_" + id }, { "Version", item.Version } };
                var clone = new Dictionary<string, object>(values);
                this.UpdateItem(id, values);

                // Второй апдейт с той же версией умрет
                Assert.Throws(typeof(VersionConflictException), () => this.UpdateItem(id, clone));
            }
        }

        public virtual void UpdateItem(Guid id, Dictionary<string, object> values)
        {
            var versionInfoItems = new List<KeyValuePair<Key, VersionInfo>>();
            if (values.ContainsKey("Version"))
            {
                var key = Key.Create(typeof(Person), id);
                var versionInfo = new VersionInfo(Xtensive.Core.Tuples.Tuple.Create((DateTime)values["Version"]));
                versionInfoItems.Add(new KeyValuePair<Key, VersionInfo>(key, versionInfo));
                values.Remove("Version");
            }

            var versionSet = new VersionSet(versionInfoItems);

            using (VersionValidator.Attach(versionSet))
            {
                using (var transaction = Transaction.Open())
                {
                    var item = Query.Single<Person>(id);
                    using (var ir = Validation.Disable())
                    {
                        foreach (var keyValuePair in values)
                        {
                            item[keyValuePair.Key] = keyValuePair.Value;
                        }

                        ir.Complete();
                    }

                    transaction.Complete();
                }
            }
        }
    }
}

using System;
using Xtensive.Storage;

namespace DoTests
{
    [Serializable]
    [HierarchyRoot]
    public class Person : Entity
    {
        public Person(Guid id)
            : base(id)
        {
        }

        [Field, Key]
        public Guid Id { get; private set; }

        [Field, Version]
        public DateTime Version { get; set; }

        /// <summary>
        /// Gets or sets Name.
        /// </summary>
        [Field]
        public string Name { get; set; }
    }
}

This thread was imported from our support forum. The original discussion may contain more detailed answer.

asked Jun 29 '10 at 09:31

xumix's gravatar image

xumix
425757682


One Answer:

Alex (Xtensive) wrote:

This bug is, likely, effect of fully fixing viewtopic.php?f=29&t=5882

The problem we have here appears because Entity.UpdateVersionInfo isn't called in this case, because SystemBeforeSetValue (its caller) isn't called as well when the same value is assigned to persistent property.

We actually have a similar test for this in our own test suite (AFAIK, in DisconnectedState tests), so it anyway must be fixed. Likely, we'll add a set of methods & events like SetValueAttempt (please try to invent a better name, otherwise I'll leave this one :) ).


Alex (Xtensive) wrote:

Fixed, as well as one more bug with serialization of DisconnectedState introduced in v4.3 RC4, that appeared as result of fixing issue with viewtopic.php?f=28&t=5933 - DisconnectedState uses it during serialization. CheckVersion test was actually exposing it - a new exception was thrown there, but I noticed this just now. Strange, but all the other tests involving serialization of DisconnectedState were passing, although the bug is pretty serious.

So returning back to the fix - now there are:

  • Persistent.OnSettingFieldValueAttempt method
  • Session.EntityFieldValueSettingAttempt event
  • UpdateVersion is invoked from Persistent.SystemFieldValueSettingAttempt, i.e. even when new value is the same as the old one.

psulek wrote:

Yes, it does - but normally with out parameter (TryGetXxx(key, out result)). So this might be confusing also... Yes you're right, name can be "SafelyTrySet" or "TrySetSafely" :-)

answered Jun 29 '10 at 16:52

Editor's gravatar image

Editor
46156156157

A bit more clear test (for these ones who interested): http://goo.gl/ch4j

(Jun 29 '10 at 16:52) Alex Yakunin Alex%20Yakunin's gravatar image

I decided to update all the installers because of this fix - both issues are important. New installers are already available.

(Jun 29 '10 at 16:52) Alex Yakunin Alex%20Yakunin's gravatar image

Great! Thanks! Btw SetValueAttempt -> TrySetValue?

(Jun 29 '10 at 16:52) xumix xumix's gravatar image

I thought about this option as well, but finally left the original one. For me they're nearly equal.

Another alternative is SetValuePreview, but this can be confusing with WPF events. Or no?

(Jun 29 '10 at 16:52) Alex Yakunin Alex%20Yakunin's gravatar image

Alex and why not TrySetValue ? If i remember right then .NET framework itself use such name somewhere.

(Jun 29 '10 at 16:52) Peter Šulek Peter%20%C5%A0ulek's gravatar image

Yes, it does - but normally with out parameter (TryGetXxx(key, out result)). So this might be confusing also...

(Jun 29 '10 at 16:52) Alex Yakunin Alex%20Yakunin's gravatar image

Err... Why "Safely"?

(Jun 29 '10 at 16:52) Alex Yakunin Alex%20Yakunin's gravatar image
Your answer
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!
toggle preview

Subscription:

Once you sign in you will be able to subscribe for any updates here

Tags:

×574

Asked: Jun 29 '10 at 09:31

Seen: 6,028 times

Last updated: Jun 29 '10 at 09:31

powered by OSQA