using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Technewlogic.ObjectLounge.Test.Model;
using System.Threading;
using NUnit.Framework;
namespace Technewlogic.ObjectLounge.Test
{
public class D_Transactions_IsolationTests : BaseTest
{
[Test]
public void ReadCommittedValue_Primitive()
{
Customer sundk = _context.Customers.Single(c => c.Name == Customer.SKName);
int originalAccount = sundk.AccountNumber;
int newAccount = 12349;
Exception exception = null;
AutoResetEvent evt1 = new AutoResetEvent(false);
AutoResetEvent evt2 = new AutoResetEvent(false);
AutoResetEvent evt3 = new AutoResetEvent(false);
AutoResetEvent endOfB = new AutoResetEvent(false);
Action a = () =>
{
var ta = _engine.BeginTransaction();
try
{
sundk.AccountNumber = newAccount;
Assert.AreEqual(newAccount, sundk.AccountNumber);
// start the other thread
evt1.Set();
// the other thread should read the "original" value...
evt2.WaitOne();
// now we commit the changes
ta.Commit();
evt3.Set();
// the other thread should read the committed value
Console.WriteLine("End Thread A");
}
catch (Exception ex)
{
exception = ex;
}
};
Action b = () =>
{
try
{
// wait for the other thread to change the value
evt1.WaitOne();
Assert.AreEqual(originalAccount, sundk.AccountNumber);
evt2.Set();
// let the other thread continue and commit
evt3.WaitOne();
Assert.AreEqual(newAccount, sundk.AccountNumber);
Console.WriteLine("End Thread B");
endOfB.Set();
}
catch (Exception ex)
{
exception = ex;
}
};
a.BeginInvoke(null, null);
b.BeginInvoke(null, null);
if (!endOfB.WaitOne(2000, false))
throw exception;
Console.WriteLine("End Main Thread");
}
[Test]
public void ReadCommittedValue_Composition()
{
Customer sundk = GetOldCustomer();
Order cmms = GetOldOrder();
Customer pizzaInder = _context.Customers.Single(c => c.Name == Customer.PizzaInderName);
Order billingSystem = pizzaInder.Orders.Single(o => o.OrderID == Order.BillingSystemID);
Order newOrder = GetNewOrder();
Exception exception = null;
AutoResetEvent evt1 = new AutoResetEvent(false);
AutoResetEvent evt2 = new AutoResetEvent(false);
AutoResetEvent evt3 = new AutoResetEvent(false);
AutoResetEvent endOfB = new AutoResetEvent(false);
Action a = () =>
{
var ta = _engine.BeginTransaction();
try
{
sundk.Orders.Add(billingSystem);
sundk.Orders.Add(newOrder);
Assert.IsTrue(sundk.Orders.Contains(billingSystem));
Assert.IsTrue(sundk.Orders.Contains(newOrder));
Assert.IsFalse(pizzaInder.Orders.Contains(billingSystem));
Assert.AreEqual(sundk, billingSystem.Customer);
Assert.AreEqual(sundk, newOrder.Customer);
// start the other thread
evt1.Set();
// the other thread should read the "original" value...
evt2.WaitOne();
// now we commit the changes
ta.Commit();
Assert.IsTrue(sundk.Orders.Contains(billingSystem));
Assert.IsTrue(sundk.Orders.Contains(newOrder));
Assert.IsFalse(pizzaInder.Orders.Contains(billingSystem));
Assert.AreEqual(sundk, billingSystem.Customer);
Assert.AreEqual(sundk, newOrder.Customer);
evt3.Set();
// the other thread should read the committed value
Console.WriteLine("End Thread A");
}
catch (Exception ex)
{
exception = ex;
}
};
Action b = () =>
{
try
{
// wait for the other thread to change the value
evt1.WaitOne();
Assert.IsFalse(sundk.Orders.Contains(billingSystem));
Assert.IsFalse(sundk.Orders.Contains(newOrder));
Assert.IsTrue(pizzaInder.Orders.Contains(billingSystem));
Assert.AreEqual(pizzaInder, billingSystem.Customer);
Assert.AreNotEqual(sundk, newOrder.Customer);
evt2.Set();
// let the other thread continue and commit
evt3.WaitOne();
Assert.IsTrue(sundk.Orders.Contains(billingSystem));
Assert.IsTrue(sundk.Orders.Contains(newOrder));
Assert.IsFalse(pizzaInder.Orders.Contains(billingSystem));
Assert.AreEqual(sundk, billingSystem.Customer);
Assert.AreEqual(sundk, newOrder.Customer);
Console.WriteLine("End Thread B");
endOfB.Set();
}
catch (Exception ex)
{
exception = ex;
}
};
a.BeginInvoke(null, null);
b.BeginInvoke(null, null);
if (!endOfB.WaitOne(2000, false))
throw exception;
Console.WriteLine("End Main Thread");
}
/// <summary>
/// Two concurrent transactions want to write same and different entities.
/// Result: The transactions are queued.
/// </summary>
[Test]
public void Write_Serialized()
{
DateTime startTime = DateTime.Now.AddDays(-1);
DateTime endRead = startTime.AddDays(-1);
DateTime endWriteA = endRead.AddDays(-1);
DateTime endWriteB = endWriteA.AddDays(-1);
DateTime endWriteC = endWriteB.AddDays(-1);
ManualResetEvent writeAStarted = new ManualResetEvent(false);
AutoResetEvent endOfWriteC = new AutoResetEvent(false);
Action writeA = () =>
{
_engine.ExecuteTransaction(() =>
{
var sundk = GetOldCustomer();
sundk.AccountNumber = 12349;
// now this transaction is the writing transaction, so we can start the other concurrent transaction
writeAStarted.Set();
Thread.Sleep(2000);
endWriteA = DateTime.Now;
});
};
Action read = () =>
{
writeAStarted.WaitOne();
var sundk = GetOldCustomer();
Assert.AreEqual(1, sundk.AccountNumber);
endRead = DateTime.Now;
};
Action writeB = () =>
{
_engine.ExecuteTransaction(() =>
{
writeAStarted.WaitOne();
var sundk = GetOldCustomer();
sundk.AccountNumber = 345;
endWriteB = DateTime.Now;
});
};
Action writeC = () =>
{
_engine.ExecuteTransaction(() =>
{
writeAStarted.WaitOne();
// wait so that the B is the next one in the queue
Thread.Sleep(500);
var sundk = GetOldCustomer();
sundk.AccountNumber = 5500;
endWriteC = DateTime.Now;
});
endOfWriteC.Set();
};
writeA.BeginInvoke(null, null);
read.BeginInvoke(null, null);
writeB.BeginInvoke(null, null);
writeC.BeginInvoke(null, null);
if (!endOfWriteC.WaitOne(5000, false))
throw new Exception();
Assert.GreaterOrEqual(endRead, startTime);
Assert.GreaterOrEqual(endWriteA, endRead);
Assert.GreaterOrEqual(endWriteB, endWriteA);
Assert.GreaterOrEqual(endWriteC, endWriteB);
var check = GetOldCustomer();
Assert.AreEqual(5500, check.AccountNumber);
}
// TODO: Read only / Write
}
}