Hi,
The following TEST throws an NullReferenceException where we expect an exception mentionning the absence of transaction around the Query.All<t>
Indeed, we didn't use SessionOptions.NonTransactionReads
---- EXCEPTION ----
Result Message:
Test method UnitTests.Orm.DataObjects.TestExceptionWithoutOpenedTransaction threw exception:
System.NullReferenceException: Object reference not set to an instance of an object.
Result StackTrace:
at Xtensive.Tuples.TupleExtensions.GetFieldStateMap(Tuple target, TupleFieldState requestedState)
at Xtensive.Orm.Providers.Persister.CreateUpdateTask(PersistAction action, Boolean validateVersion)
at Xtensive.Orm.Providers.Persister.CreatePersistTask(PersistAction action, Boolean validateVersion)
at Xtensive.Orm.Providers.Persister.Persist(EntityChangeRegistry registry, CommandProcessor processor)
at Xtensive.Orm.Providers.SqlSessionHandler.Persist(EntityChangeRegistry registry, Boolean allowPartialExecution)
at Xtensive.Orm.Session.Persist(PersistReason reason)
at Xtensive.Orm.Session.CreateEnumerationContext()
at Xtensive.Orm.Rse.ProviderExtensions.GetRecordSet(ExecutableProvider provider, Session session)
at Xtensive.Orm.Linq.TranslatedQuery`1.Execute(Session session, ParameterContext parameterContext)
at Xtensive.Orm.Linq.QueryProvider.Execute[TResult](Expression expression)
at System.Linq.Queryable.Count[TSource](IQueryable`1 source)
at UnitTests.Orm.DataObjects.TestExceptionWithoutOpenedTransaction() in c:\Work-Dev\aradus\Projets\DonatelloBranches\DataObjects5\UnitTests\Orm\DataObjects.cs:line 130
---- CODE ----
namespace UnitTests.Orm
{
[TestClass]
public class DataObjects
{
[TestMethod, TestCategory("Transaction.Rollback")]
public void TestExceptionWithoutOpenedTransaction()
{
Domain domain = TestUtils.CreateDomain(SessionOptions.ValidateEntities | SessionOptions.AutoActivation | SessionOptions.AutoSaveChanges);
using (Session session = domain.OpenSession())
{
using (TransactionScope t = session.OpenTransaction())
{
new TestEntity();
t.Complete();
}
Assert.IsTrue(Query.All<TestEntity>().Count() == 1);
}
}
}
public static class TestUtils
{
public static 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.TransactionPrecommitting += new EventHandler<TransactionEventArgs>(Session_TransactionPrecommitting);
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_TransactionPrecommitting(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
Jun 02 '17 at 10:49
Benoit Nesme
43●20●20●24