Wednesday, April 14, 2010

WCF RIA, Repository pattern and testability (Part 2)

Ok, let’s continue…

The idea of testability based on the ability to substitute execution context. And we usually achieve that by using IoC containers. However, I am strong follower of concept “Simplest thing that works” that’s why I introduce you to DataContext

Code Snippet
/// <summary>
/// Keeps ObjectContext for current request
/// </summary>
public static class DataContext
{
    [ThreadStatic] private static IObjectContext _current;

    public static IObjectContext Current
    {
        get { return _current; }
        internal set { _current = value; } // must be set only from BaseDomainService or UnitTestBase
    }
}

as you can see it’s just simplest implementation of Abstract Singleton and it can be easily substituted for our testing purposes. For “real” environment we can assign it here

Code Snippet
/// <summary>
/// Provides base repository and transactional support + common entity processing before save
/// </summary>
/// <typeparam name="T">ObjectContext</typeparam>
public abstract class DomainServiceBase<T> : LinqToEntitiesDomainService<T>
    where T : ObjectContext, IObjectContext, new()
{
    public override void Initialize(DomainServiceContext context)
    {
        base.Initialize(context);
        DataContext.Current = ObjectContext;
    }

For “test” environment here

Code Snippet
[TestClass]
public abstract class UnitTestBase
{
    [TestInitialize]
    public void MyTestInitialize()
    {
        DataContext.Current = PrepareDataContext();
    }

    protected abstract IObjectContext PrepareDataContext();
}

the last thing we need to do is to implement “test” versions of our main EF related abstractions IObjectSet and IObjectContext

Code Snippet
public class FakeObjectSet<TEntity> : IObjectSet<TEntity> where TEntity : class
{
    private readonly HashSet<TEntity> _data;
    private readonly IQueryable _query;

    public FakeObjectSet() : this(new List<TEntity>())
    {
    }

    public FakeObjectSet(IEnumerable<TEntity> testData)
    {
        _data = new HashSet<TEntity>(testData);
        _query = _data.AsQueryable();
    }

    #region IObjectSet<TEntity> Members

    public void AddObject(TEntity item)
    {
        _data.Add(item);
    }

    public void DeleteObject(TEntity item)
    {
        _data.Remove(item);
    }

    public void Detach(TEntity item)
    {
        _data.Remove(item);
    }

    public void Attach(TEntity item)
    {
        _data.Add(item);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return _data.GetEnumerator();
    }

    IEnumerator<TEntity> IEnumerable<TEntity>.GetEnumerator()
    {
        return _data.GetEnumerator();
    }

    Type IQueryable.ElementType
    {
        get { return _query.ElementType; }
    }

    Expression IQueryable.Expression
    {
        get { return _query.Expression; }
    }

    IQueryProvider IQueryable.Provider
    {
        get { return _query.Provider; }
    }

    #endregion
}

the code speaks for itself – our FakeObjectSet is just banal wrapper around HashSet. Next comes the sweet FakeDb

Code Snippet
public class FakeDb : IObjectContext
{
    protected Dictionary<Type, object> Sets = new Dictionary<Type, object>();

    public void AddEntity<T>(T entity) where T : EntityObject
    {
        if (entity == null) return;

        CreateObjectSet<T>().Attach(entity);
    }

    public void AddEntities<T>(IEnumerable<T> entities) where T : EntityObject
    {
        if (entities == null) return;

        foreach (var entity in entities)
        {
            AddEntity(entity);
        }
    }

    public IObjectSet<T> CreateObjectSet<T>() where T : class
    {
        object set;
        if (!Sets.TryGetValue(typeof(T), out set))
        {
            set = new FakeObjectSet<T>();
            Sets.Add(typeof(T), set);
        }
        return (FakeObjectSet<T>)set;
    }

    // override to provide some real DB like behaviour (ex: IDs generation)
    public virtual int SaveChanges()
    {
        return 1; // what a surprise ;)
    }

    public void Clear()
    {
        Sets.Clear();
    }
}

again – simple, straightforward implementation – our FakeDB is just a set of FakeObjectSets or “in memory DB-like structure” for simplicity. Well, so far so good, let’s write our first Unit Test

Code Snippet
[TestClass]
public class UnitTestExample : UnitTestBase
{
    protected override IObjectContext PrepareDataContext()
    {
        // for integration-style UT with real DB
        // return new TestEntities();
        var db = new FakeDb();
        db.AddEntity(new Order { Name = "Test" });
        db.AddEntity(new Order { Name = "Test1" });
        return db;
    }

    [TestMethod]
    public void TestMethod1()
    {
        var ordersRepository = new RepositoryBase<Order>();
        Assert.AreEqual(2, ordersRepository.Query.Count());
        var order = ordersRepository.Query.Where(c => c.Name == "Test").SingleOrDefault();
        Assert.IsNotNull(order);
    }
}

Great result – with the minimum amount of plumbing we substituted our real DB with in-memory twin that we can fill with test data we need. And if you prefer integration-style unit test with real test DB, you can easily do that as well. And of course all standard CRUD will work ;)

Friday, April 9, 2010

WCF RIA, Repository pattern and testability

When you developing WCF RIA domain services you usually highly coupled with underlying data access layer (DAL) technology of your choice. It’s usually Entity Framework (EF), LinqToSQL, nHibernate, pure ADO.NET or other ORM/DAL frameworks. It’s great, however it brings following issues

  1. Learning curve for some ORM/DAL frameworks can be painful (it may include convoluted APIs with leaking abstractions)
  2. Due to coupling it’s hard/impossible to switch to new DAL framework if you bump into some major issues with you current one
  3. Lack of testability with some of ORM/DAL frameworks

That’s why it makes sense to introduce another level of abstraction/indirection between your domain logic and DAL. It must be simple, testable and well known concept. Well, and we have one – it’s famous Repository pattern.

There are many ways you can define/implement it, let’s consider my version. In this example we will implement generic repository for EF’s entities. First of all – let’s define abstraction

Code Snippet
public interface IRepository<T> where T : EntityObject
{
    // Query
    IQueryable<T> Query { get; }

    // CRUD
    void Insert(T entity);
    void Update(T entity);
    void Delete(T entity);
}

As you can see our repository is quite succinct. It can do two things – Query for data and CRUD. And it seems that learning curve for using this abstraction is minimal (read - developers will love it)

As long as we trying to adapt standard code generated by EF designer for our repository needs we need to introduce some more interfaces to abstract away our custom ObjectContext

Code Snippet
public interface IUnitOfWork
{
    int SaveChanges();
}

This is abstraction for EF’s ObjectContext which is essentially implementation of UnitOfWork (UoW) pattern. However it’s not only UoW, it’s also entity sets container, which can be abstracted away by

Code Snippet
public interface IObjectContext : IUnitOfWork
{
    IObjectSet<T> CreateObjectSet<T>() where T : class;
}

ok, so far so good, now we need to make our custom ObjectContext (generated by EF designer) to implement this abstraction. It can be easily done by partial class like this

Code Snippet
public partial class TestEntities : IObjectContext
{
    public new IObjectSet<T> CreateObjectSet<T>() where T : class
    {
        return base.CreateObjectSet<T>();
    }
}

great, now we can implement our generic repository

Code Snippet
/// <summary>
/// Base repository, provides basic CRUD, Query and IUnitOfWork
/// Be aware, that unit of work is shared across all repositories,
/// so first SaveChanges() will save all changes made by all repositories before
/// </summary>
/// <typeparam name="T">Type of entity you working with</typeparam>
public class RepositoryBase<T> : IRepository<T>, IUnitOfWork where T : EntityObject
{
    private readonly IObjectContext _context;
    private readonly IObjectSet<T> _objectSet;

    public RepositoryBase()
    {
        if (DataContext.Current == null) throw new Exception("DataContext is undefined");
        _context = DataContext.Current;
        _objectSet = _context.CreateObjectSet<T>();
    }

    #region IRepository<T> Members

    public IQueryable<T> Query
    {
        get { return _objectSet; }
    }

    public virtual void Insert(T entity)
    {
        if (entity.EntityState == EntityState.Added) return;
        if (entity.EntityState == EntityState.Unchanged)
        {
            var ctx = _context as ObjectContext;
            if (ctx == null) return;
            ctx.ObjectStateManager.ChangeObjectState(entity, EntityState.Added);
        }
        else
        {
            _objectSet.AddObject(entity);
        }
    }

    public virtual void Update(T entity)
    {
        var ctx = _context as ObjectContext;
        if (ctx == null) return;
        if (entity.EntityState == EntityState.Detached) ctx.Attach(entity);
        ctx.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
    }

    public virtual void Delete(T entity)
    {
        if (entity.EntityState == EntityState.Detached) _objectSet.Attach(entity);
        _objectSet.DeleteObject(entity);
    }

    #endregion

    #region IUnitOfWork Members

    public virtual int SaveChanges()
    {
        // Update common fields
        var ctx = _context as ObjectContext;
        if (ctx != null)
        {
            var changes = ctx.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified);
            foreach (var entity in changes.Select(objectStateEntry => objectStateEntry.Entity))
            {
                UpdateHelper.UpdateCommonFields(entity);
            }
        }
        return _context.SaveChanges();
    }

    #endregion
}

As you can see our generic repository mixes Repository and UnitOfWork patterns, which will allow us to do batch data modifications and save in one go.

The usage of this generic class is simple, elegant and straightforward

Code Snippet
var orderRepository = new RepositoryBase<Order>();
var order = orderRepository.Query.First(o => o.ID == id);
order.Name = "Modified on server!";
orderRepository.SaveChanges();

You may wonder what is that magical DataContext our Repository depends on? Well, wait for the next post ;)

to be continued…

Monday, April 5, 2010

RIA and DTO (Part 2)

Ok, let’s make our DTO a bit more complex (or more real life like I would say)

Code Snippet
public class MyCustomDTO
{
    [Key]
    public int ID { get; set; }

    public int MyPayload { get; set; }
    public string MyOtherPayload { get; set; }
    
    [Include]
    [Association("MyCustomDTO_MyItem", "ID", "ParentID")]
    public IEnumerable<MyItem> MyItems { get; set; }
}

public class MyItem
{
    [Key]
    public int ID { get; set; }
    public int ParentID { get; set; }

    public string Name { get; set; }
    public int Value { get; set; }
}

so we added list of MyItems to our DTO. We also marked this list with [Include] and [Association]. It’s very important part and soon you’ll see why. Let’s test our DTO, is it really able to transfer data to/from client? Server-side is

Code Snippet
[Invoke]
public MyCustomDTO MyOtherOperation(MyCustomDTO data)
{
    data.MyItems = new List<MyItem> { new MyItem { Name = "new", Value = 72 } };
    return data;
}

Note: for some strange reason RIA won’t allow you to use sets/enumerations of DTO as parameter, so the following will not compile. Be aware!

Code Snippet
[Invoke]
public void MyOtherOperation(IEnumerable<MyCustomDTO> data)

the workaround is simple – just encapsulate your list inside another DTO class ;)

ok, now client side

Code Snippet
var data = new MyCustomDTO
{
    MyPayload = 13,
    MyOtherPayload = "Test"
};
ctx.MyOtherOperation(data,
    op =>
    {
        var result = op.Value;
        Debug.Assert(result.MyItems.Count == 1);
    },
    null);

Let’s run it… boom! Assertion failed! We expected our list be non empty but it is ;( You might ask – why? We did all good server side. Well.. not really ;) Here comes very important part – how client client side code generated by RIA handle references. It does handle it via associations. The same way as relational databases handle foreign key associations. So to make it work you have to provide valid values of properties that make association. Here is the code it uses internally (generated client side)

Code Snippet
[Association("MyCustomDTO_MyItem", "ID", "ParentID")]
public EntityCollection<MyItem> MyItems
{
    get
    {
        if ((this._myItems == null))
        {
            this._myItems = new EntityCollection<MyItem>(this, "MyItems", this.FilterMyItems);
        }
        return this._myItems;
    }
}

and the magic happens here

Code Snippet
private bool FilterMyItems(MyItem entity)
{
    return (entity.ParentID == this.ID);
}

so, as you now see, to fix our problem we have to do this

Code Snippet
var data = new MyCustomDTO
{
    ID  = 1,
    MyPayload = 13,
    MyOtherPayload = "Test",
};
data.MyItems.Add(new MyItem { ID = 1, ParentID = 1, Name = "client", Value = 5 });

Well, let’s give it another go. F5… same result! The sad truth is that unfortunately at the moment [Invoke] operations don't support complex composite DTOs. The only place you can use it is either [Query] methods (as a result) or in Submit based operations. Which means that we can either wait till RIA team fixes this awkward functional gap or we could use normal WCF service or this simple but ugly workaround

Server side

Code Snippet
[Insert]
public void MyItemDummyInsert(MyItem data) { }

[Insert]
public void MyOperation(MyCustomDTO data)

Client side

Code Snippet
var ctx = (OrderContext)orderDomainDataSource.DomainContext;
var data = new MyCustomDTO
{
    ID  = 1,
    MyPayload = 13,
    MyOtherPayload = "Test",
};
data.MyItems.Add(new MyItem { ID = 1, ParentID = 1, Name = "client", Value = 5 });
ctx.MyCustomDTOs.Add(data);
ctx.SubmitChanges();

the idea here is pretty banal – use part of Submit processing as our custom operation.

The most painful limitation of using DTOs in WCF RIA is that you can’t use DTOs as [Query] methods parameters

Code Snippet
public IQueryable<TestData> MyQuery(MyCustomDTO data)

will produce following compilation error

Parameter 'data' of domain operation entry 'MyQuery' must be one of the predefined serializable types.

Why??? Why RIA team has decided that providing only basic types as parameters would be enough? Take a look outside your Ivory Tower guys – there is real world outside and developers do need complex query parameters (you just can’t express everything with IQueryable<T> and simple typed parameters)

The workaround is as usual simple and ugly – you can expose your parameter as string, serialize your DTO on client and de-serialize on server

Code Snippet
public IQueryable<TestData> TestDataSearch(string searchCriteria)

Ok, to summarize – by now RIA has very limited support for passing DTOs back and forth. We can pray and wait for this to be fixed or use dirty workarounds.

Happy RIA coding, to be continued…