Tag Archives: Domain Service

Use Transactions and Rollback for Unit Testing against WCF RIA Services

Apparently Silverlight provide use of WCF RIA Services as a data access tool, it behaves just like an ORM tool but over the wire.

How it works ? Well, it names express quite straightforward. It has WCF as interface to let client request and response the data. At the client side, it does generate sort of proxy as same as regular WCF or even old-fashioned web service does. It uses Entity Framework as an ORM tool to manipulate database.

In this post, I will assume that we are going to write unit test against server operations. So proxy and communication over the wire thing will not be considered.

The approach that I’m going to show you is pretty much the same as my previous post “Use Transactions and Rollback for Unit Testing against Entity Framework“, because as I mentioned that RIA uses Entity Framework as ORM tool.

Note that you will need to keep classes from 1-3 in RIA Services project, 4-5 in Test Project.

1. Create a new class that override DBTransaction class.

  • Make the constructor to accept a parameter of DbTransaction object. So this class acts just like an adapter of DbTransaction class.
  • By pass the real rollback and commit behavior by overriding Rollback and Commit functions so actual rollback and commit functions will not do anything.
  • Create RollbackUnitTest function to be called from another class that we are going to write it up next.
public class TestDbTransaction : System.Data.Common.DbTransaction
{
   public DbTransaction ActualDBTransaction { get; private set; }

   public TestDbTransaction(DbTransaction actualDBTransaction)
   {
      ActualDBTransaction = actualDBTransaction;
   }

   public override void Rollback()
   {
      // do nothing : the test transaction is already running
   }

   public override void Commit()
   {
      // do nothing : the test transaction is already running
   }

   public void RollbackUnitTest()
   {
      ActualDBTransaction.Rollback();
   }

   protected override System.Data.Common.DbConnection DbConnection
   {
      get { return ActualDBTransaction.Connection; }
   }

   public override IsolationLevel IsolationLevel
   {
      get { return ActualDBTransaction.IsolationLevel; }
   }
}

2.  Create extension methods of ObjectContext named BeginTransaction to begin the transaction and return TestDbTransaction object out.

  public static class TestObjectContextExtender
  {
        public static System.Data.Common.DbTransaction BeginTransaction(this System.Data.Objects.ObjectContext context, IsolationLevel isolationLevel)
        {
            var actualTransaction = context.Connection.BeginTransaction(isolationLevel);
            return new TestDbTransaction(actualTransaction);
        }

        public static System.Data.Common.DbTransaction BeginTransaction(this System.Data.Objects.ObjectContext context)
        {
            return new TestDbTransaction(context.Connection.BeginTransaction());
        }
  }

3. Create a partial class of Domain Service.
We need to get access to the ObjectContext which has access level as protected. So we can either override it or create partial class. I go with partial and make extension method to avoid lots of changes.

Don’t forget to replace YOURDomainService and YOUREntities with yours.


    public partial class YOURDomainService : LinqToEntitiesDomainService<YOUREntities>
    {
        public TestDbTransaction BeginTransactionForUnitTest()
        {
            this.ObjectContext.Connection.Open();
            return this.ObjectContext.BeginTransaction() as TestDbTransaction;
        }

        public YOUREntities GetDataContext()
        {
            return this.ObjectContext;
        }
    }

4. Create a base class to be inherited by each unit test class.

TestStart method is being called before every unit test and TestEnd after. This is the result of pasting attribute TestInitialize and TestCleanup on them.

    [TestClass()]
    public class TransactedTestClass
    {
        protected GeneralDomainService _service;
        private TestDbTransaction _transaction;

        [TestInitialize()]
        public void TestStart()
        {
            DomainServiceContext context = new DomainServiceContext(new ServiceProvider(), DomainOperationType.Query);
            _service= new GeneralDomainService();
            _service.Initialize(context);

            _transaction = _service.BeginTransactionForUnitTest();
        }

        [TestCleanup()]
        public void TestEnd()
        {
            _transaction.RollbackUnitTest();
        }
    }

5. Sample of use. Create a test class and inherit from TransactedTestClass.

  • _service is an instance declared in TransactedTestClass.
  • GetDataContext method is declared in partial class in 3.
[TestClass()]
public class GeneralDomainServiceTest : TransactedTestClass
{
        [TestMethod()]
        public void AddNewUser_Test()
        {
            // Get ObjectContext for validation (directly query to database).
            var dataContext = _service.GetDataContext();
            // Assume that our test database has no record in Users table
            Assert.AreEqual(0, dataContext.Users.Count());
            _service.AddNewUser(new User());
            // Verify that one record is added
            Assert.AreEqual(1, dataContext.Users.Count());
        }
}
Tagged , , , , , , , ,

Filter Sub Entity in Query of LinqToEntities DomainService

If you are one of the developers, who are working on the project that use Domain Service with LinqToEntities, this common issue should annoy you sometimes.

Normally we use Include(“RelatedEntity”) to tell LinqToEntitiesDomainService to also retrieve ALL records of RelatedEntity that have relation to the main querying entity.

For example with this data:
Customer Table

CustomerId Name Sex
1 Mr. Wise Male
2 Mrs. Fool Female

Order Table

OrderId CustomerId Total PaymentTypeId
1 1 $5000 1
2 1 $200 1

PaymentType Table

PaymentTypeId PaymentTypeName
1 Cash
2 Credit Card

With this sample data, if we need all Male Customers along with their Orders. It would look like this.

var customers = from c in this.ObjectContext.Customers.Include("Orders")
                where c.CustomerId == 1
                select c;

But what if we need all Male Customers along with their Orders that Total over $1000 ONLY ?

Well, there is no regular operation to embed condition into the query for filtering out the sub entities. The Where statement only filters the main entity, the Customer in this case.

There is a workaround by modify query with this tricky query.

var customers = (from c in this.ObjectContext.Customers
                 where c.CustomerId == 1
                 select new {
                   MaleCustomer = c,
                   OrderOverThousand = c.Orders.Where(o => o.Total > 1000)
                 }).ToList().Select(c=>c.MaleCustomer);

Note that you do not need to have Include operation for this query. It does populate all entities you specified in the enclosed anonymous class after the Select statement.

You may find this workaround in several blogs but I couldn’t find two level depth of related entity.

With the same query condition but also expect PaymentType of Orders, it would look like this:

var customers = (from c in this.ObjectContext.Customers
                 where c.CustomerId == 1
                 let filteredOrders = c.Orders.Where(o => o.Total > 1000)
                 select new {
                   MaleCustomer = c,
                   OrderOverThousand = filteredOrders,
                   OrderPaymentType = filteredOrders.Select(o => o.PaymentType)
                 }).ToList().Select(c=>c.MaleCustomer);

As you can see, all you have to do is just put another property of anonymous class as many entities as you want. You may doubt, “What does the let statement do?” and “Why do we need it?”. It is optional but it can save you from unnecessary addition Select operation when the context transforms it into the SQL statement.

As you can see at line number 6 and 7, without let statement, it would be c.Orders.Where(o => o.Total > 1000) instead of the filteredOrders variable.  As a result, both will do Select operation twice for the same result set.

Hope this post can elucidate some who struggle in such problems 😉 .

Tagged , , , , , ,