1
1

I have found several questions related to System.DateTime which cover a similar problem. However, it seemed none of them really initialized something.

I am using an sqlserverce connection. You can find a sample application that demonstrates the problem here. I must note that I do not have this problem when using a memory database.

My Player class contains a DateTime field.

[HierarchyRoot]
internal class Player : Entity
{
    public Player(Session session)
        : base(session)
    {
        Created = DateTime.Now;
    }

    [Field]
    public DateTime Created { get; set; }
}

I have a function to get a Player object and create a new Player object. (As I cannot access the Session object from outside the service that holds these query functions.)

public List<Player> GetPlayers(Func<Player, bool> predicate)
{
    IEnumerable<Player> result = _connection.Session.Query.Execute(
        q => q.All<Player>().Where(predicate));

    return result.Count() > 0 ? result.ToList() : new List<Player>(0);
}

public Player GetNewPlayer()
{
    return new Player(_connection.Session);
}

In my final application I have two functions which are more or less similar to this.

public void DoSomething()
{
    Player p1 = _service.GetPlayers(p => p.Name.Equals("first")).FirstOrDefault();
    if (p1 == null) {
        p1 = _service.GetNewPlayer();
        p1.Name = "first";
        _service.Save();
    }

    Player p2 = _service.GetPlayers(p => p.Name.Equals("second")).FirstOrDefault();
    if (p2 == null) {
        p2 = _service.GetNewPlayer();
        p2.Name = "second";
        _service.Save();
    }
}

When I attempt to retrieve the second player the following exception is thrown:

A first chance exception of type 'Xtensive.Orm.VersionConflictException' occurred in Xtensive.Orm.dll
Version of entity with key 'Player, (1)' differs from the expected one.

Now, from what I understand from the other issues (which are quite old and based on older DO versions) it's because an incorrect DateTime value is used to initialize this. Bugs have been filed but I have not found any bug that was fixed or any mention of a solution. I could of course create my own private structures to handle a DateTime object properly and use the OnInitialize() override to convert those into a System.DateTime, but that seems like a lot of unneeded work.

What's the proper solution to handle DateTime objects?

asked Apr 13 '11 at 06:22

jensen's gravatar image

jensen
399913

Hello jensen,

I'll check the sample you provided. Thanks for that!

(Apr 13 '11 at 09:52) Dmitri Maximov Dmitri%20Maximov's gravatar image

One Answer:

Hello jensen,

As you use SessionOptions.ClientProfile, DataObjects.Net utilizes entity versions for optimistic concurrency control support. By default, all non-lazy fields are used to produce a version of an entity (a snapshot of it), but it is better to set up version-related fields explicitly. For example, in your model I'd recommend to follow this approach:

[HierarchyRoot]
internal class Player : Entity {
  ...

  [Field]
  [Version(VersionMode.Auto)]
  public int Version { get; set;}
}

Thus, VersionInfo would include only 1 int field, moreover, this would save you from the VersionConflictException you were being disturbed by.

More on versions & optimistic concurrency

answered Apr 18 '11 at 07:01

Dmitri%20Maximov's gravatar image

Dmitri Maximov
22111211

Should this be done for entities and structures, or is it enough that I only do this for entities?

(Apr 27 '11 at 05:03) jensen jensen's gravatar image

Entities are enough. You may inherit all your entities from the one, (say, MyRootEntity) where you could define those version-related stuff once.

(Apr 27 '11 at 05:05) Dmitri Maximov Dmitri%20Maximov's gravatar image

When doing some changes to an object today I ran into an issue. When calling Session.SaveChanges() I received a SavePoint error. After checking the logs it mentioned a version conflict for my object (Version ('123'), version ('23') expected).

I managed to solve this by setting the version mode to Manual, but I wonder what implications this might have?

(Jun 24 '11 at 05:12) jensen jensen's gravatar image

Hello jensen,

There is a protected virtual method Entity.UpdateVersion which is called when an Entity is about to be modified. In VersionMode.Auto the value of version field is incremented automatically, whereas in VersionMode.Manual you should manage the version value by yourself.

Hope that helps.

(Jun 24 '11 at 05:26) Dmitri Maximov Dmitri%20Maximov's gravatar image

Yes, I already came to the conclusion this is not really what I want to do. Yet, I'm not quite sure why it would throw an exception like that. I'm creating a new object, open the view and let the user fill in his contact details, and call Session.SaveChanges() when the user clicks save.

When I code-wise create an entire user in the same way I'm not getting an exception. But the investigation is ongoing!

(Jun 24 '11 at 05:42) jensen jensen's gravatar image

OK, keep the community informed. Good luck.

(Jun 24 '11 at 05:48) Dmitri Maximov Dmitri%20Maximov's gravatar image

I found the issue, I think.

  1. I open a ClientProfile session (.Recreate), create an object and save it.
  2. I then build a new domain with a ServerProfile, fill the database and dispose the domain. (I use .Skip or .PerformSafely)
  3. I then use the ClientProfile session to create a new object.

The error I get is that the version of an object created in step 1 does not match the expected one.

If I skip step two there's no problem. If I change step two so that it uses the same domain as in step 1, I get the same error.

(Jun 24 '11 at 06:46) jensen jensen's gravatar image

jensen,

could you share a sample or something so I could reproduce the case on my side and investigate it deeper?

Thanks

(Jun 24 '11 at 06:52) Dmitri Maximov Dmitri%20Maximov's gravatar image

Allright, I finally managed to produce a sample application demonstrating the problem. You can download it at http://bit.ly/mOzKL5.

(Jun 24 '11 at 09:09) jensen jensen's gravatar image

Many thanks!

(Jun 24 '11 at 09:10) Dmitri Maximov Dmitri%20Maximov's gravatar image

Any update on this? I have a similar issue.

(Jun 28 '11 at 09:41) TimM TimM's gravatar image

Not yet. May be closer to the weekend or right after.

(Jun 28 '11 at 11:08) Dmitri Maximov Dmitri%20Maximov's gravatar image

Hello jensen, TimM,

The investigation revealed that User type is subscribed on Session.Persisting event and updates its state while Session is persisting. This causes version conflict. I'd recommend you to update the User.PCountry property value inside User.Country setter. This scenario is more clear and works without any issues.

private RegionInfo country;
public RegionInfo Country
{
    get { return country; }
    set {
          country = value; 
          if (country != null)  
            PCountry = country.Name;
        }
}
(Jul 03 '11 at 08:12) Dmitri Maximov Dmitri%20Maximov's gravatar image

Thanks for the reply.

Will the same conflict arise with collections which are persisted to a byte array? (As documented http://blog.dataobjects.net/2011/01/how-to-handle-non-persistent-collection.html) As you can imagine it would be quite time and memory consuming when I have to persist the entire collection of 10.000 elements each time an element is added.

(Jul 03 '11 at 11:59) jensen jensen's gravatar image

Hello jensen,

You are right, the scenario I described doesn't add sense while working with such collections. On the other hand you might try the approach with overriding Persistent.UpdateVersion method. Here is the sample:

class User : Entity
  private bool skipVersionUpdate;

private void OnPersisting(object sender, EventArgs e) {
    skipVersionUpdate = true;
    PCountry = Country.Name;
    skipVersionUpdate = false;
}

override bool UpdateVersion(...)
{
  if (skipVersionUpdate)
    return false;

  return base.UpdateVersion(...);
}
(Jul 04 '11 at 06:17) Dmitri Maximov Dmitri%20Maximov'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

powered by OSQA