Hi,

I have the following bug with DataObjects 5 and the new SessionOptions. It quite hard to reproduce because it needs to have :

  • SessionOptions.AutoSaveChanges | SessionOptions.NonTransactionalReads
  • events attached to session.SystemEvents
  • a ref<ientity> object
  • no Query.All<testentity> at some points

Please let me know if you can reproduce it as well

-- CODE

namespace UnitTests.Orm
{
  [TestClass]
  public class DataObjects
  {
    [TestMethod, TestCategory("Transaction.Rollback")]
    public void TestTransactionRollbackWithAutoSaveChangesAndRef()
    {
      // Removing SessionOptions.NonTransactionalReads will make the test pass
      Domain domain = CreateDomain(SessionOptions.ValidateEntities | SessionOptions.AutoActivation | SessionOptions.AutoSaveChanges | SessionOptions.NonTransactionalReads);
      using (Session session = domain.OpenSession())
      {
        Ref<TestEntity> refTestEntity;
        using (TransactionScope t = session.OpenTransaction())
        {
          TestEntity testEntity = new TestEntity(){IsCompressed = false};
          refTestEntity = (Ref<TestEntity>) testEntity;
          t.Complete();
        }

        // Uncommenting the line below will make the test pass : 
        // Query.All<TestEntity>().ToList();

        using (TransactionScope t = session.OpenTransaction())
        {
          TestEntity testEntity = (TestEntity) refTestEntity;
          testEntity.IsCompressed = true;
          // do not complete
        }
        using (TransactionScope t = session.OpenTransaction())
        {
          TestEntity testEntity = (TestEntity)refTestEntity;
          Assert.IsTrue(testEntity.IsCompressed == false);
          t.Complete();
        }
      }
    }

    private Domain CreateDomain(SessionOptions sessionOptions)
    {
      string connectionString = Configuration.Instance.DataObjectsConnectionString;

      DomainConfiguration config = new DomainConfiguration(connectionString);
      config.UpgradeMode = DomainUpgradeMode.Recreate;
      config.Types.Register(typeof(TestEntity).Assembly);

      SessionConfiguration defaultSessionConfig = config.Sessions.Default;
      if (defaultSessionConfig == null)
      {
        defaultSessionConfig = new SessionConfiguration(WellKnown.Sessions.Default);
        config.Sessions.Add(defaultSessionConfig);
      }

      defaultSessionConfig.Options = sessionOptions;
      ConnectionInfo connectionInfo = new ConnectionInfo(connectionString);
      string sqlConnectionString = new Xtensive.Sql.Drivers.SqlServer.DriverFactory().GetConnectionString(connectionInfo);
      using (System.Data.SqlClient.SqlConnection connection = new System.Data.SqlClient.SqlConnection(sqlConnectionString))
      using (DbCommand command = connection.CreateCommand())
      {
        connection.Open();
        command.CommandText = "Select * From sys.fulltext_catalogs";
        bool ftCatalogExists = false;
        using (DbDataReader reader = command.ExecuteReader())
        {
          ftCatalogExists = reader.HasRows;
        }

        if (!ftCatalogExists)
        {
          // full text catalog does not exists : creates it
          command.CommandText = "CREATE FULLTEXT CATALOG [Default] " + "WITH ACCENT_SENSITIVITY = ON " + "AS DEFAULT " + "AUTHORIZATION [dbo]";
          command.ExecuteNonQuery();

          command.CommandText = "ALTER FULLTEXT CATALOG [Default] REBUILD ";
          command.ExecuteNonQuery();
        }
      }
      return Domain.Build(config);
    }
  }

   [Serializable]
  [HierarchyRoot]
  public class TestEntity : Entity, IHasFullTextIndex
  {
    [Field, Key]
    public long Id { get; private set; }

    [Field]
    public bool IsFtEnabled { get; set; }

    [Field]
    public bool? IsFtIndexUpToDate { get; set; }

    [Field]
    public string FullText { get; set; }

    [Field]
    public bool IsCompressed { get; set; }
  }

   public interface IHasFullTextIndex : IEntity
  {
    [Field]
    long Id { get; }

    [Field]
    bool IsFtEnabled { get; set; }

    [Field]
    bool? IsFtIndexUpToDate { get; set; }

    [Field(Length = Int32.MaxValue)]
    [FullText("French")]
    string FullText { get; set; }
  }

  public class EntityChangeWatcherFullText : IModule
  {
    public void OnDefinitionsBuilt(Xtensive.Orm.Building.BuildingContext context, Xtensive.Orm.Building.Definitions.DomainModelDef model)
    {

    }

    public void OnBuilt(Domain domain)
    {
      domain.SessionOpen += new EventHandler<SessionEventArgs>(domain_SessionOpen);
    }

    private void domain_SessionOpen(object sender, SessionEventArgs e)
    {
      // a session has been created
      e.Session.SystemEvents.TransactionOpened += new EventHandler<TransactionEventArgs>(Session_TransactionOpened);
      e.Session.SystemEvents.TransactionCommitting += new EventHandler<TransactionEventArgs>(Session_TransactionCommitting);
      e.Session.SystemEvents.EntityCreated += new EventHandler<EntityEventArgs>(Session_EntityCreated);
      e.Session.SystemEvents.EntityVersionInfoChanged += new EventHandler<EntityVersionInfoChangedEventArgs>(Session_EntityVersionInfoChanged);
    }

    private void Session_TransactionOpened(object sender, TransactionEventArgs e)
    {
      // transaction opened : create our list to track entity changes
      Transaction transaction = e.Transaction;
      if (transaction.IsNested)
        return;
      Session session = transaction.Session;
      if (session.IsDisconnected)
        return;

      session.Extensions.Set(new EntityChangeWatcherInfo());
    }

    private void Session_EntityCreated(object sender, EntityEventArgs e)
    {
      // entity created
      MarkEntityInSessionListOfEntitiesToIndex(e.Entity);
    }

    private void Session_EntityVersionInfoChanged(object sender, EntityVersionInfoChangedEventArgs e)
    {
      // entity has been modified, or a sub object in an entitysert has been changed
      MarkEntityInSessionListOfEntitiesToIndex(e.Entity);
    }

    private void MarkEntityInSessionListOfEntitiesToIndex(Entity entity)
    {
      // entity to list of entity to mark fot ft indexing for current session
      // (only if necessary)
      try
      {
        Session session = entity.Session;
        if (session.IsDisconnected)
          return;

        EntityChangeWatcherInfo info = session.Extensions.Get<EntityChangeWatcherInfo>();
        if (info == null)
          return;
        if (info.IgnoreThisSession)
          return;

        if (!info.EntitiesToMarkForFullTextIndexing.Contains(entity.Key))
        {
          info.EntitiesToMarkForFullTextIndexing.Add(entity.Key);
        }
      }
      catch
      {
      }
    }

    private void Session_TransactionCommitting(object sender, TransactionEventArgs e)
    {
      // Just before transaction commit, read the list of entities changed for session
      // and for each entity set IsFtIndexUpToDate to false, so that we can asynchronously refresh it
      Transaction transaction = e.Transaction;
      if (transaction.IsNested)
        return;
      Session session = transaction.Session;
      if (session.IsDisconnected)
        return;

      EntityChangeWatcherInfo info = session.Extensions.Get<EntityChangeWatcherInfo>();
      if (info == null)
        return;
      if (info.IgnoreThisSession)
        return;

      foreach (Key key in info.EntitiesToMarkForFullTextIndexing)
      {
        Entity entity = session.Query.SingleOrDefault(key);
        // entity may have been removed
        if (entity != null)
        {
          IHasFullTextIndex ftObject = entity as IHasFullTextIndex;
          if (ftObject != null && ftObject.IsFtIndexUpToDate != false)
          {
            ftObject.IsFtIndexUpToDate = false;
          }
        }
      }
      info.EntitiesToMarkForFullTextIndexing.Clear();
    }
  }

  class EntityChangeWatcherInfo
  {
    public bool IgnoreThisSession { get; set; }

    public HashSet<Key> EntitiesToMarkForFullTextIndexing = new HashSet<Key>();
  }
}

asked Aug 30 '16 at 09:42

Benoit%20Nesme's gravatar image

Benoit Nesme
13111316

edited Aug 30 '16 at 09:46


One Answer:

Hello Benoit Nesme

Thank you for the bug report. I successfully reproduced the problem.

answered Aug 31 '16 at 06:13

Alexey%20Kulakov's gravatar image

Alexey Kulakov
45215

Nice, let me know when you have a fix

(Aug 31 '16 at 06:47) Benoit Nesme Benoit%20Nesme's gravatar image

Hi Alexey, Have you made any progress on this issue ? This is blocking us to migrate to DO5

(Sep 20 '16 at 05:16) Benoit Nesme Benoit%20Nesme'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:

×4
×2
×1

Asked: Aug 30 '16 at 09:42

Seen: 1,073 times

Last updated: Sep 20 '16 at 05:16

powered by OSQA