Introduction:
In my previous post I talked about definitions of TDD as specified in different sources. Here in this post I'll walk-through applied Domain Driven Design & Test Driven Design on Entity Framework.
I assume that you already know Entity Framework and how to create your entity data modes using Visual Studio.Net 2008 with SP1.
Domain Driven Design & Entity Framework:
To simplify things, I am using Northwind database. Basically I'll expose 3 tables in this database, Categories, Products & Suppliers. Those will be my domain objects as Category,Product and Supplier classes.
Each Category object might contain one or more items of Product. Same thing for Supplier object as it might provide one or more items of Product. That means Product object must have a Supplier and a Category. I defined those criteria for my Domain Objects.
I started to create my Domain Object using Entity Framework. My Entity Data Model looks like the below diagram:
I'm not done yet. I thought that just using my generated code is not enough. And I want to abstract my Domain Object as much as possible incase I want to use different tool other than Entity Framework such as LINQ to SQL.
So I created 3 contracts (interfaces), that actually represents my Domain Objects: ICategory, IProduct and ISupplier. These contracts has parent interface called IEntity that has only one property called Id. Assuming that all my Domain Objects should have a unique key property of type integer and called Id.
I made my generated models to implement those interfaces (thanks to partial classes). In fact those interfaces contains exactly same property names and types as specified in the above models. Except that I renamed the navigation properties in the generated models to include the word Internal such as ProductsInternal navigation property in Category concrete data model class.
Below are the definition of each interface:
public interface IEntity
{
int Id { get; }
}
public interface ICategory : IEntity
{
string CategoryName { get; set; }
string Description { get; set; }
byte[] Picture { get; set; }
IQueryable<IProduct> Products { get; }
}
public interface IProduct : IEntity
{
string ProductName { get; set; }
string QuantityPerUnit { get; set; }
decimal? UnitPrice { get; set; }
short? UnitsInStock { get; set; }
short? UnitsOnOrder { get; set; }
short? ReorderLevel { get; set; }
bool Discontinued { get; set; }
ICategory Category { get; }
ISupplier Supplier { get; }
}
public interface ISupplier : IEntity
{
string CompanyName { get; set;}
string ContactName { get; set; }
string ContactTitle { get; set; }
string Address { get; set; }
string Country { get; set; }
string City { get; set; }
string Region { get; set; }
string PostalCode { get; set; }
string Phone { get; set; }
string Fax { get; set; }
string HomePage { get; set; }
IQueryable<IProduct> Products { get; }
}
I would like to clarify few points about the above code. As you notice on lines 11, 23, 24 and 40. These lines of code define navigation properties. For example ICategory domain object interface has a navigation property IQueryable<IProduct> Products that retrieves all products related to a specific category. I made this to be independent of the under laying generated code by Entity Framework. This is actually abstraction and removing dependency between your Domain Object Definitions and its implementations. The definitions should define what these models should contain and do. The implementation should specify the how. Your concrete models might be coupled with certain technology, as in my case. My concrete models are Entity Framework Models.
Entity Framework as Data Access Layer:
Entity Framework provide Data Services classes that I will use as my data access layer assets. For example the generated ObjectContext class, this is actually acts as my proxy to the underlying datasource and called entity container by Entity Framework Schema. But Entity Framework doesn't support -in version 1- persistence ignorance! Which will make working with my Domain Object Model interfaces hard and may be impossible. Because for example the generate ObjectContext class doesn't have anything that would return a result set of ICategory. And guess what you'll not be able to cast ObjectQuery<Category> to IQueryable<ICategory>.
Take this as an example the generate NorthwindDataContext has a property called CategorySet. This property returns ObjectQuery<Category>. You might think of calling this property like this:
context.CategorySet.Cast<ICategory>().AsQueryable();
But this is simply will not work and will throw an exception as Cast extension method will only work if you are casting to correct generate Entity Models primitive types.
Another issue calling CategorySet property or calling CreateQuery<T> method of the base ObjectContext class would result in creating a query that will require hitting the database when starting to iterate through the results. This issue violates one of the guide lines of performing TDD which is to be independent of the working environment by simulating environmental dependencies such as databases, file systems, networks, queues etc...
To over come these issues simply I created another interface that include basic data access functionality. I learned this idea from Kigg the open source project that is running live on http://dotnetshoutout.com/. This project apply the same idea on Linq To SQL context object class.
I called the interface IDataContext and defined the basic operations I will need in this interface:
public interface IDataContext
{
IQueryable<ICategory> Categories { get; }
IQueryable<IProduct> Products { get; }
IQueryable<ISupplier> Suppliers { get; }
IQueryable<TInterface> GetDataSource<TInterface, TEntity>(Func<TEntity, bool> filterExpression)
where TInterface : class, IEntity
where TEntity : class;
TEntityInterface GetEntityById<TEntityInterface>(int id)
where TEntityInterface : class, IEntity;
void Insert<TEntityInterface>(TEntityInterface instance)
where TEntityInterface : class, IEntity;
void Delete<TEntityInterface>(TEntityInterface instance)
where TEntityInterface : class, IEntity;
void SubmitChanges();
}
Again the generated ObjectContext class by Entity Framework should implement this interface. Below is the implementation of IDataContext:
public partial class NorthwindDataContext : IDataContext
{
public static IEnumerable<TInterface> GetEnumerable<TInterface, TEntity>(IQueryable<TEntity> query, Func<TEntity, bool> filter)
where TInterface : class, IEntity
where TEntity : class
{
var filteredQuery = (filter != null) ?
(from item in query select item).Where(filter) : query;
foreach (var item in filteredQuery)
{
yield return item as TInterface;
}
}
public virtual IQueryable<Category> CategoryTable
{
get{return this.CategorySet;}
}
public virtual IQueryable<Product> ProductTable
{
get{return this.ProductSet;}
}
public virtual IQueryable<Supplier> SupplierTable
{
get{return this.SupplierSet;}
}
public IQueryable<ICategory> Categories
{
get
{
return this.GetDataSource<ICategory, Category>(null);
}
}
public IQueryable<IProduct> Products
{
get
{
return this.GetDataSource<IProduct, Product>(null);
}
}
public IQueryable<ISupplier> Suppliers
{
get
{
return this.GetDataSource<ISupplier, Supplier>(null);
}
}
public IQueryable<TInterface> GetDataSource<TInterface, TEntity>(Func<TEntity, bool> filter)
where TInterface : class, IEntity
where TEntity : class
{
IQueryable<TEntity> queryable = null;
if (typeof(TInterface) == typeof(ICategory))
queryable = this.CategoryTable as IQueryable<TEntity>;
if (typeof(TInterface) == typeof(IProduct))
queryable = this.ProductTable as IQueryable<TEntity>;
if (typeof(TInterface) == typeof(ISupplier))
queryable = this.SupplierTable as IQueryable<TEntity>;
return GetEnumerable<TInterface, TEntity>(queryable, filter).AsQueryable() as IQueryable<TInterface>;
}
public TEntity GetEntityById<TEntity>(int id)
where TEntity : class, IEntity
{
if (typeof(TEntity) == typeof(ICategory))
{
return this.GetDataSource<ICategory,Category>(c => c.Id == id).SingleOrDefault() as TEntity;
}
else if (typeof(TEntity) == typeof(IProduct))
{
return this.GetDataSource<IProduct,Product>(c => c.Id == id).SingleOrDefault() as TEntity;
}
else if (typeof(TEntity) == typeof(ISupplier))
{
return this.GetDataSource<ISupplier, Supplier>(c => c.Id == id).SingleOrDefault() as TEntity;
}
else
return null;
}
public void Insert<TEntity>(TEntity instance)
where TEntity : class, IEntity
{
AddObject<TEntity>(instance);
}
public void Delete<TEntity>(TEntity instance)
where TEntity : class, IEntity
{
DeleteObject(instance);
}
public virtual void AddObject<TEntity>(TEntity instance)
where TEntity : class, IEntity
{
Type type = typeof(TEntity);
if (type == typeof(ICategory))
{
this.AddToCategorySet(instance as Category);
}
else if (type == typeof(IProduct))
{
this.AddToProductSet(instance as Product);
}
else if (type == typeof(ISupplier))
{
this.AddToSupplierSet(instance as Supplier);
}
}
public new virtual void DeleteObject(object instance)
{
base.DeleteObject(instance);
}
public void SubmitChanges()
{
base.SaveChanges(true);
}
}
An important thing to note, now after implementing IDataContext, from my code I should use NorthwindDataContext as IDataContext. Which means my consumer application which could be a Repository class or anything else should not know anything about NorthwindDataContext and be totally independent of it.
Using NorthwindDataContext as my data access layer through IDataContext is something that can be specified using Dependency Injection.
Start Updated Content
I would like to explain 2 methods above which are IEnumerable<TInterface> GetEnumerable<TInterface, TEntity>(IQueryable<TEntity> query, Func<TEntity, bool> filter) and IQueryable<TInterface> GetDataSource<TInterface, TEntity>(Func<TEntity, bool> filter)...
GetEnumerable method is helper method, I used it to overcome casting limitations in LINQ to Entities. This method takes IQueryable<TEntity> as a parameter. TEntity is supposed to be the concrete model class while IQueryable<TEntity> will be actually ObjectQuery<TEntity>. This method is used by XxxDataSource properties. It will help me much in testing my Data Access Layer as I will show in the next part soon.
About GetDataSource method, this one also to overcome LINQ to Entities limitations. In fact in implementation GetDataSource makes call to GetEnumerable and pass a filter expression to it. Someone would say why Filter as you could use property Categories and build a LINQ statement over it and that is it, something like context.Categories.Where(c=>c.Id == 1); Well, I highly recommend to review Davy Landman comment on this post which shows that the code snippet context.Categories.Where(c=>c.Id == 1) would perform filter in memory and not on database side. Maybe I should call this method GetFilteredDataSource.
End Updated Content
I didn't complete the implementation of my domain model objects, basically the navigation properties. There are different ideas to implement them so I will keep that to be done later to differentiate between different approaches I have in mind.
What about TDD first?!
You might ask why I didn't start with TDD as supposed?! Well, I had an idea that my domain object should be ready before starting writing testing code! Someone would say write test and then refactor! I say 2 faces one coin. I made my design, and I will write its Test every time I finish part of it. Now that I finished this part, I would start writing my unit test code. In next part I will talk about Unit Testing with VS.Net tools and Moq. Applying TDD to my design to ensure that my code works fine with my expectations. And later I will do another unit testing for integration to ensure that both TDD and Integration Testing where both built on the correct assumptions.
Moq will help me to achieve 3 guide lines:
- Separates or simulates environmental dependencies such as databases, file systems, networks, queues, and so on. Tests that exercise these will not run fast, and a failure does not give meaningful feedback about what the problem actually is.
- Runs and passes in isolation. If the tests require special environmental setup or fail unexpectedly, then they are not good unit tests. Change them for simplicity and reliability. Tests should run and pass on any machine.
- Often uses stubs and mock objects. If the code being tested typically calls out to a database or file system, these dependencies must be simulated, or mocked. These dependencies will ordinarily be abstracted away by using interfaces
Moq will help me to set expectations for my returning results of certain methods. For example GetEnumerable method I mentioned above should return IEnumerable<TInterface>. I can simulate its operation using Moq and set an expectation to its call where expected and also define the return results. To simulate database results I will use in memory collections which I will specify as return results whenever possible with the assist of Moq. We will look at what Moq can provide us in my next post.
You can download my initiative project and Stay Tuned for the upcoming part.