February 2009 - Posts

In my previous post, I have shown how to create Linq to Sql Repository which will have the maximum code coverage, In this post, I will show a simple UnitOfWork class which will flash the changes back to your database. I will be again use my ongoing UnityCommonServiceLocatorMVC project. Lets assume that in your ASP.NET MVC application you have a method in your controller which will add a category and its associate product in the Northwind database.

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Submit(string categoryName, string productName, decimal? unitPrice)
{
    Category category = new Category
                            {
                                CategoryName = categoryName
                            };

    _categoryRepository.Add(category);

    Product product = new Product
                          {
                              ProductName = productName,
                              UnitPrice = unitPrice,
                              Category = category
                          };

    _productRepository.Add(product);

    return View();
}

If you run the above code you will find nothing happens as we are not committing the changes back to the database. Now, lets create the UnitOfWork which will flush the changes:

public interface IUnitOfWork
{
    void Commit();
}

public class UnitOfWork : IUnitOfWork
{
    private readonly IDatabase _database;

    public UnitOfWork(IDatabase database)
    {
        _database = database;
    }

    public void Commit()
    {
        _database.SubmitChanges();
    }
}

Now we can rewrite the Controller Submit method like the following which will commit the changes back to database(line 20):

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Submit(string categoryName, string productName, decimal? unitPrice)
{
    Category category = new Category
                            {
                                CategoryName = categoryName
                            };

    _categoryRepository.Add(category);

    Product product = new Product
                          {
                              ProductName = productName,
                              UnitPrice = unitPrice,
                              Category = category
                          };

    _productRepository.Add(product);

    _unitOfWork.Commit();

    return View();
}

But still it is not convincing, I would like to show explicitly which part of my code is under the unit of work, lets do some refactoring of the UnitOfWork:

public interface IUnitOfWork : IDisposable
{
    void Commit();
}

public class UnitOfWork : IUnitOfWork
{
    private readonly IDatabase _database;

    public UnitOfWork(IDatabase database)
    {
        _database = database;
    }

    public static IUnitOfWork Begin()
    {
        return ServiceLocator.Current.GetInstance<IUnitOfWork>();
    }

    public void Commit()
    {
        _database.SubmitChanges();
    }

    public void Dispose()
    {
    }
}

Now we can write the submit method like the following:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Submit(string categoryName, string productName, decimal? unitPrice)
{
    using (IUnitOfWork unitOfWork = UnitOfWork.Begin())
    {
        Category category = new Category
                                {
                                    CategoryName = categoryName
                                };

        _categoryRepository.Add(category);

        Product product = new Product
                              {
                                  ProductName = productName,
                                  UnitPrice = unitPrice,
                                  Category = category
                              };

        _productRepository.Add(product);

        unitOfWork.Commit();
    }

    return RedirectToAction("Index");
}

Similar kind of Transaction.Begin() and Transaction.Commit(). Next, the unit test of controller:

public class HomeControllerTest
{
    private readonly Mock<ICategoryRepository> _categoryRepository;
    private readonly Mock<IProductRepository> _productRepository;
    private readonly Mock<IUnitOfWork> _unitOfWork;

    private readonly HomeController _controller;

    public HomeControllerTest()
    {
        _categoryRepository = new Mock<ICategoryRepository>();
        _productRepository = new Mock<IProductRepository>();
        _unitOfWork = new Mock<IUnitOfWork>();

        MockServiceLocator();

        _controller = new HomeController(_categoryRepository.Object, _productRepository.Object);
    }

    [Fact]
    public void Submit_Should_Add_Category()
    {
        Submit();

        _categoryRepository.Verify();
    }

    [Fact]
    public void Submit_Should_Add_Product()
    {
        Submit();

        _productRepository.Verify();
    }

    [Fact]
    public void Submit_Should_Flush_Changes()
    {
        Submit();

        _unitOfWork.Verify();
    }

    private void Submit()
    {
        _categoryRepository.Expect(r => r.Add(It.IsAny<Category>())).Verifiable();
        _productRepository.Expect(r => r.Add(It.IsAny<Product>())).Verifiable();
        _unitOfWork.Expect(uow => uow.Commit()).Verifiable();

        _controller.Submit("Dummy Category", "Dummy Product", 10);
    }

    private void MockServiceLocator()
    {
        var serviceLocator = new Mock<IServiceLocator>();

        serviceLocator.Expect(s => s.GetInstance<IUnitOfWork>()).Returns(_unitOfWork.Object);

        ServiceLocator.SetLocatorProvider(() => serviceLocator.Object);
    }
}

And at last the mapping of UnitOfWork:

string connectionString = ConfigurationManager.ConnectionStrings["ApplicationServices"].ConnectionString;

IUnityContainer container = new UnityContainer();

container.RegisterType<IDatabase, Database>(new UnityPerWebRequestLifetimeManager(), new InjectionConstructor(new [] { connectionString }))
         .RegisterType<IUnitOfWork, UnitOfWork>(new UnityPerWebRequestLifetimeManager())
         .RegisterType<ICategoryRepository, CategoryRepository>(new UnityPerWebRequestLifetimeManager())
         .RegisterType<IProductRepository, ProductRepository>(new UnityPerWebRequestLifetimeManager())
         .RegisterType<IControllerFactory, CommonServiceLocatorControllerFactory>(new ContainerControlledLifetimeManager())
         .RegisterType<IBootstrapperTask, RegisterRoutes>("route", new ContainerControlledLifetimeManager())
         .RegisterType<IBootstrapperTask, RegisterControllerFactory>("controllerFactory", new ContainerControlledLifetimeManager())
         .RegisterType<IFormsAuthentication, FormsAuthenticationService>(new ContainerControlledLifetimeManager())
         .RegisterType<IMembershipService, AccountMembershipService>(new InjectionConstructor())
         .RegisterType<AccountController>()
         .RegisterType<HomeController>();

ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(container));

Check that we are using the UnityPerWebRequestLifetimeManager for UnitOfWork(line 6) which we have created in the earlier post, if you set any other lifetime manager it will not work.

Download: UnitOfWork.zip

Shout it

In this post, I will show you how you can architect your Linq To Sql repository which will have 100% code coverage. I will use our favorite Northwind database along with my ongoing UnityCommonServiceLocatorMVC project. First, lets add a Linq To Sql diagram in our project and drag n drop the Category and Product table that will look something like following:

Northwind-L2S

We are skipping the other tables as they are same that we are doing here, next we will create an interface which will abstract the Linq To Sql DataContext from our application.

public interface IDatabase : IDisposable
{
    IQueryable<Category> CategoryDataSource
    {
        get;
    }

    IQueryable<Product> ProductDataSource
    {
        get;
    }

    IQueryable<TEntity> GetQueryable<TEntity>() where TEntity : class;

    ITable GetEditable<TEntity>() where TEntity : class;

    void Insert<TEntity>(TEntity instance) where TEntity : class;

    void InsertAll<TEntity>(IEnumerable<TEntity> instances) where TEntity : class;

    void Delete<TEntity>(TEntity instance) where TEntity : class;

    void DeleteAll<TEntity>(IEnumerable<TEntity> instances) where TEntity : class;

    void SubmitChanges();
}

And now we will create a partial class for the DataContext that implements this interface:

public partial class Database : IDatabase
{
    public IQueryable<Category> CategoryDataSource
    {
        get
        {
            return GetQueryable<Category>();
        }
    }

    public IQueryable<Product> ProductDataSource
    {
        get
        {
            return GetQueryable<Product>();
        }
    }

    public virtual IQueryable<TEntity> GetQueryable<TEntity>() where TEntity : class
    {
        return GetTable<TEntity>();
    }

    public virtual ITable GetEditable<TEntity>() where TEntity : class
    {
        return GetTable<TEntity>();
    }

    public void Insert<TEntity>(TEntity instance) where TEntity : class
    {
        GetEditable<TEntity>().InsertOnSubmit(instance);
    }

    public void InsertAll<TEntity>(IEnumerable<TEntity> instances) where TEntity : class
    {
        GetEditable<TEntity>().InsertAllOnSubmit(instances);
    }

    public void Delete<TEntity>(TEntity instance) where TEntity : class
    {
        GetEditable<TEntity>().DeleteOnSubmit(instance);
    }

    public void DeleteAll<TEntity>(IEnumerable<TEntity> instances) where TEntity : class
    {
        GetEditable<TEntity>().DeleteAllOnSubmit(instances);
    }
}

By Default Linq To Sql exposes Sql Server tables as Table which is a sealed class, so we can not mock it. But Linq to Sql namespace has an interface ITable which the Table type implements and that is easy to mock, also this ITable exposes InsertOnSubmit, InsertAllOnSubmit, DeleteOnSubmit, DeleteAllOnSubmit. So in our interface we exposed the ITable instead of that concrete Table which makes this partial Database class completely unit testable. We will be using Moq and xUnit.net for our unit tests but you can also use RhinoMock, NUnit, MBUnit or MSTest.

public class DatabaseTest
{
    private readonly Mock<Database> _database;

    public DatabaseTest()
    {
        _database = new Mock<Database>("A dummy connection string");
    }

    [Fact]
    public void CategoryDataSource_Should_Call_GetQueryable()
    {
        _database.Expect(d => d.GetQueryable<Category>()).Returns(new List<Category>().AsQueryable()).Verifiable();

        Assert.NotNull(_database.Object.CategoryDataSource);

        _database.Verify();
    }

    [Fact]
    public void ProductDataSource_Should_Call_GetQueryable()
    {
        _database.Expect(d => d.GetQueryable<Product>()).Returns(new List<Product>().AsQueryable()).Verifiable();

        Assert.NotNull(_database.Object.ProductDataSource);

        _database.Verify();
    }

    [Fact]
    public void Insert_Should_Call_GetEditable_And_InsertOnSubmit()
    {
        var editable = new Mock<ITable>();
        var category = new Category();

        _database.Expect(d => d.GetEditable<Category>()).Returns(editable.Object).Verifiable();
        editable.Expect(e => e.InsertOnSubmit(category)).Verifiable();

        _database.Object.Insert(category);

        _database.Verify();
        editable.Verify();
    }

    [Fact]
    public void InsertAll_Should_Call_GetEditable_And_InsertAllOnSubmit()
    {
        var editable = new Mock<ITable>();
        var categories = new List<Category>();

        _database.Expect(d => d.GetEditable<Category>()).Returns(editable.Object).Verifiable();
        editable.Expect(e => e.InsertAllOnSubmit(categories)).Verifiable();

        _database.Object.InsertAll(categories);

        _database.Verify();
        editable.Verify();
    }

    [Fact]
    public void Delete_Should_Call_GetEditable_And_DeleteOnSubmit()
    {
        var editable = new Mock<ITable>();
        var category = new Category();

        _database.Expect(d => d.GetEditable<Category>()).Returns(editable.Object).Verifiable();
        editable.Expect(e => e.DeleteOnSubmit(category)).Verifiable();

        _database.Object.Delete(category);

        _database.Verify();
        editable.Verify();
    }

    [Fact]
    public void DeleteAll_Should_Call_GetEditable_And_DeleteAllOnSubmit()
    {
        var editable = new Mock<ITable>();
        var categories = new List<Category>();

        _database.Expect(d => d.GetEditable<Category>()).Returns(editable.Object).Verifiable();
        editable.Expect(e => e.DeleteAllOnSubmit(categories)).Verifiable();

        _database.Object.DeleteAll(categories);

        _database.Verify();
        editable.Verify();
    }
}

Now, lets create the repositories which uses this database:

public interface IProductRepository
{
    void Add(Product product);

    void Remove(Product product);

    Product FindById(int id);

    ICollection<Product> FindByCategoryId(int categoryId);

    ICollection<Product> FindAll();
}

public class ProductRepository : IProductRepository
{
    private readonly IDatabase _database;

    public ProductRepository(IDatabase database)
    {
        _database = database;
    }

    public void Add(Product product)
    {
        _database.Insert(product);
    }

    public void Remove(Product product)
    {
        _database.Delete(product);
    }

    public Product FindById(int id)
    {
        return _database.ProductDataSource.SingleOrDefault(p => p.ProductID == id);
    }

    public ICollection<Product> FindByCategoryId(int categoryId)
    {
        return _database.ProductDataSource.Where(p => p.CategoryID == categoryId).ToList().AsReadOnly();
    }

    public ICollection<Product> FindAll()
    {
        return _database.ProductDataSource.ToList().AsReadOnly();
    }
}

And the Unit Test for it:

public class ProductRepositoryTest
{
    private readonly Mock<IDatabase> _database;
    private readonly ProductRepository _repository;

    public ProductRepositoryTest()
    {
        _database = new Mock<IDatabase>();
        _repository = new ProductRepository(_database.Object);
    }

    [Fact]
    public void Add_Should_Use_Database()
    {
        _database.Expect(db => db.Insert(It.IsAny<Product>())).Verifiable();

        _repository.Add(new Product());

        _database.Verify();
    }

    [Fact]
    public void Remove_Should_Use_Database()
    {
        _database.Expect(db => db.Delete(It.IsAny<Product>())).Verifiable();

        _repository.Remove(new Product());

        _database.Verify();
    }

    [Fact]
    public void FindById_Should_Return_Correct_Product_For_The_Specified_Id()
    {
        SetupProductDataSource();

        var product = _repository.FindById(3);

        Assert.Equal(3, product.ProductID);
    }

    [Fact]
    public void FindByCategoryId_Should_Return_Correct_Products_For_The_Specified_CategoryId()
    {
        SetupProductDataSource();

        var products = _repository.FindByCategoryId(2);

        Assert.Equal(2, products.Count);
    }

    [Fact]
    public void FindAll_Should_All_Products()
    {
        SetupProductDataSource();

        var products = _repository.FindAll();

        Assert.Equal(5, products.Count);
    }

    private void SetupProductDataSource()
    {
        Category category1 = new Category{ CategoryID = 1, CategoryName = "Test 1" };
        Category category2 = new Category{ CategoryID = 2, CategoryName = "Test 2" };

        List<Product> products = new List<Product>
                                        {
                                            new Product{ ProductID = 1, ProductName = "Test 1", Category = category1},
                                            new Product{ ProductID = 2, ProductName = "Test 2", Category = category1},
                                            new Product{ ProductID = 3, ProductName = "Test 3", Category = category1},
                                            new Product{ ProductID = 4, ProductName = "Test 4", Category = category2},
                                            new Product{ ProductID = 5, ProductName = "Test 5", Category = category2}
                                        };

        _database.ExpectGet(db => db.ProductDataSource).Returns(products.AsQueryable());
    }
}

I am skipping the CategoryRepository as it almost identical, but you can check it in the attached source codes at the bottom of this post. If I run these unit tests with NCover, we will get the following picture:

Northwind-NCover

As you can see that we have 100% coverage for both ProductRepository and CategoryRepository but 56% for the Database. But if you look it closely, you will find that we have 100% code coverage except the GetQueryable and GetEditable methods in the Database partial class .

Download: LinqTSqlCodeCoverage.zip

Shout it

In this post, I will show how you can use a simple bootstrapper to make your application more extensible. In a typical ASP.NET MVC application you will find there are lots of thing going on in Application_Start, like configuring your IoC/DI, registering routes, model binders etc. The following is the code snippet of latest Oxite source:

protected virtual void OnStart()
{
    setupContiner();

    setupSite();

    registerRoutes();

    registerActionFilters();

    registerModelBinders();

    registerViewEngines();

    registerControllerFactory();

    launchBackgroundServices();
}

Don't you think there are too many things going on, Is there any way to extend it, how do you add new routes/model binder/view engine without modifying it, does not it violet the Open Closed Principle?

The Bootstrapper

The bootstrapper should have only one responsibility, execute the start up tasks, so first lets create a Task interface:

public interface IBootstrapperTask
{
    void Execute();
}

 

Next, create few small classes for each kind of task which implements this interface, I will use my previous blog post example in this case:

RegisterControllerFactory:

public class RegisterControllerFactory : IBootstrapperTask
{
    private readonly IControllerFactory _controllerFactory;

    public RegisterControllerFactory(IControllerFactory controllerFactory)
    {
        _controllerFactory = controllerFactory;
    }

    public void Execute()
    {
        ControllerBuilder.Current.SetControllerFactory(_controllerFactory);
    }
}

RegisterRoutes:

public class RegisterRoutes : IBootstrapperTask
{
    private readonly RouteCollection _routes;

    public RegisterRoutes() : this(RouteTable.Routes)
    {
    }

    public RegisterRoutes(RouteCollection routes)
    {
        _routes = routes;
    }

    public void Execute()
    {
        _routes.Clear();

        _routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        _routes.MapRoute("Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "" });
    }
}

As you can understand there can be more, for example, I am using it in DotNetShoutout for creating default users, running background tasks etc. Next create a static class Bootstrapper which will execute these tasks:

public static class Bootstrapper
{
    static Bootstrapper()
    {
        ConfigureContainer();
    }

    public static void Run()
    {
        var tasks = ServiceLocator.Current.GetAllInstances<IBootstrapperTask>();

        foreach(var task in tasks)
        {
            task.Execute();
        }
    }

    private static void ConfigureContainer()
    {
        IUnityContainer container = new UnityContainer();

        UnityConfigurationSection configuration = (UnityConfigurationSection) ConfigurationManager.GetSection("unity");
        configuration.Containers.Default.Configure(container);

        ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(container));
    }
}

And in the Global.asax:

protected void Application_Start()
{
    Bootstrapper.Run();
}

 

And in the web.config, we can wired the tasks like the following:

<unity>
    <typeAliases>
        <typeAlias alias="IControllerFactory" type="System.Web.Mvc.IControllerFactory, System.Web.Mvc"/>
        <typeAlias alias="ControllerFactory" type="UnityCommonServiceLocatorMVC.CommonServiceLocatorControllerFactory, UnityCommonServiceLocatorMVC"/>
        <typeAlias alias="IBootstrapperTask" type="UnityCommonServiceLocatorMVC.IBootstrapperTask, UnityCommonServiceLocatorMVC"/>
        <typeAlias alias="RegisterRoutes" type="UnityCommonServiceLocatorMVC.RegisterRoutes, UnityCommonServiceLocatorMVC"/>
        <typeAlias alias="RegisterControllerFactory" type="UnityCommonServiceLocatorMVC.RegisterControllerFactory, UnityCommonServiceLocatorMVC"/>
    </typeAliases>
    <containers>
        <container>
            <types>
                <type name="registerRoutes" type="IBootstrapperTask" mapTo="RegisterRoutes"/>
                <type type="IControllerFactory" mapTo="ControllerFactory"/>
                <type name="registerControllerFactory" type="IBootstrapperTask" mapTo="RegisterControllerFactory">
                    <typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration">
                        <constructor>
                            <param name="controllerFactory" parameterType="IControllerFactory">
                                <dependency/>
                            </param>
                        </constructor>
                    </typeConfig>
                </type>
            </types>
        </container>
    </containers>
</unity>

So by using this bootstrapper we are making our application completely extensible, we can create new task and execute it in the application start without modifying anything.

Comments/Suggestions?

Download: Complete Source

Shout it

In this post, I will show you how can you extend Microsoft Patterns & Practices Unity Application Block and use it in ASP.NET MVC in conjunction with Patterns & Practices Common Service Locator.

Extending Unity

Unity is a dependency injection container from MS Patterns & Practices team. But unlike the other containers it does not have any PerWebRequest support, which means you cannot get the same object in the same web request, but it has excellent extensibility support which we can utilize to fill the gap.  The latest version of Unity has the following life time support for object instance:

  • ContainerControlledLifetimeManager: Usually used for making an instance Singleton.
  • ExternallyControlledLifetimeManager: Returns the same instance but keeps a weak reference so that object can garbage collected.
  • PerThreadLifetimeManager: Returns the same object for the same thread.
  • If no lifetime manager is specified Unity will return new instance each time it is requested.

To create a new life time manager we have to create a new class which inherits from abstract LifeTimeManager and override three methods, since we want the Unity container to return the same object for the same web request we will use the HttpContext.Items. We will also use the .NET Framework 3.5 SP1 System.Web.Abstraction to make it unit testable, code:

namespace UnityCommonServiceLocatorMVC
{
    using System;
    using System.Collections.Generic;
    using System.Web;

    using Microsoft.Practices.Unity;

    public class UnityPerWebRequestLifetimeManager : LifetimeManager
    {
        private HttpContextBase _httpContext;

        //Need this constructor for Unit Test
        public UnityPerWebRequestLifetimeManager(HttpContextBase httpContext)
        {
            _httpContext = httpContext;
        }

        public UnityPerWebRequestLifetimeManager(): this(new HttpContextWrapper(HttpContext.Current))
        {
        }

        private IDictionary<UnityPerWebRequestLifetimeManager, object> BackingStore
        {
            get
            {
                _httpContext = (HttpContext.Current != null) ? new HttpContextWrapper(HttpContext.Current) : _httpContext;

                return UnityPerWebRequestLifetimeModule.GetInstances(_httpContext);
            }
        }

        private object Value
        {
            get
            {
                IDictionary<UnityPerWebRequestLifetimeManager, object> backingStore = BackingStore;

                return backingStore.ContainsKey(this) ? backingStore[this] : null;
            }
            set
            {
                IDictionary<UnityPerWebRequestLifetimeManager, object> backingStore = BackingStore;

                if (backingStore.ContainsKey(this))
                {
                    object oldValue = backingStore[this];

                    if (!ReferenceEquals(value, oldValue))
                    {
                        IDisposable disposable = oldValue as IDisposable;

                        if (disposable != null)
                        {
                            disposable.Dispose();
                        }

                        if (value == null)
                        {
                            backingStore.Remove(this);
                        }
                        else
                        {
                            backingStore[this] = value;
                        }
                    }
                }
                else
                {
                    if (value != null)
                    {
                        backingStore.Add(this, value);
                    }
                }
            }
        }

        public override object GetValue()
        {
            return Value;
        }

        public override void SetValue(object newValue)
        {
            Value = newValue;
        }

        public override void RemoveValue()
        {
            Value = null;
        }
    }
}

Nothing special, we are overriding GetValue, SetValue, RemoveValue and created a private property which is called from these methods. There are two points I want to highlight, first the BackingStore property, although we are injecting the HttpContext in the constructor but we are creating a new HttpContext in case we are running in the production mode, otherwise it will pin down the constructor injected HttpContext and behave incorrectly. Next, for returning the BackingStore I am using an helper class, this idea is actually brought from the Castle Windsor. Creating a LifetimeManger does serves our purpose completely, we also have to ensure that disposable objects are disposed when the web request completes to avoid memory leak. And to do this we will create an HttpModule and hook its EndRequest event to do the cleanup, code:

namespace UnityCommonServiceLocatorMVC
{
    using System;
    using System.Collections.Generic;
    using System.Web;

    public class UnityPerWebRequestLifetimeModule : IHttpModule
    {
        private static readonly object Key = new object();
        private HttpContextBase _httpContext;

        //Need this constructor for Unit Test
        public UnityPerWebRequestLifetimeModule(HttpContextBase httpContext)
        {
            _httpContext = httpContext;
        }

        public UnityPerWebRequestLifetimeModule()
        {
        }

        internal IDictionary<UnityPerWebRequestLifetimeManager, object> Instances
        {
            get
            {
                _httpContext = (HttpContext.Current != null) ? new HttpContextWrapper(HttpContext.Current) : _httpContext;

                return (_httpContext == null) ? null : GetInstances(_httpContext);
            }
        }

        void IHttpModule.Init(HttpApplication context)
        {
            context.EndRequest += (sender, e) => RemoveAllInstances();
        }

        void IHttpModule.Dispose()
        {
        }

        internal static IDictionary<UnityPerWebRequestLifetimeManager, object> GetInstances(HttpContextBase httpContext)
        {
            IDictionary<UnityPerWebRequestLifetimeManager, object> instances;

            if (httpContext.Items.Contains(Key))
            {
                instances = (IDictionary<UnityPerWebRequestLifetimeManager, object>) httpContext.Items[Key];
            }
            else
            {
                lock (httpContext.Items)
                {
                    if (httpContext.Items.Contains(Key))
                    {
                        instances = (IDictionary<UnityPerWebRequestLifetimeManager, object>) httpContext.Items[Key];
                    }
                    else
                    {
                        instances = new Dictionary<UnityPerWebRequestLifetimeManager, object>();
                        httpContext.Items.Add(Key, instances);
                    }
                }
            }

            return instances;
        }

        internal void RemoveAllInstances()
        {
            IDictionary<UnityPerWebRequestLifetimeManager, object> instances = Instances;

            if (instances != null)
            {
                foreach (KeyValuePair<UnityPerWebRequestLifetimeManager, object> entry in instances)
                {
                    IDisposable disposable = entry.Value as IDisposable;

                    if (disposable != null)
                    {
                        disposable.Dispose();
                    }
                }

                instances.Clear();
            }
        }
    }
}

We are maintaining a Dictionary against a Key in the HttpContext.Items which holds the Lifetime manager and its corresponding value as key value pair and in the end request we are iterating this dictionary and calling the dispose method.

Common Service Locator

The Common Service Locator is an abstraction over the actual container so that our application does not make hard dependency on any specific container. Currently the Common Service Locator has support for Windsor, StructureMap, Spring.NET, Autofac, Unity and MEF.

Integrate with ASP.NET MVC

We will use the default ASP.NET MVC project to demonstrate the integration. Assuming our HomeController depends upon  CategoryRepository and ProductRepository, code:

private readonly ICategoryRepository _categoryRepository;
private readonly IProductRepository _productRepository;

public HomeController(ICategoryRepository categoryRepository, IProductRepository productRepository)
{
    _categoryRepository = categoryRepository;
    _productRepository = productRepository;
}

Next, we will have to inject these dependency and for this we will create a ControllerFactory:

namespace UnityCommonServiceLocatorMVC
{
    using System;
    using System.Web.Mvc;

    using Microsoft.Practices.ServiceLocation;

    public class CommonServiceLocatorControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(Type controllerType)
        {
            return (controllerType == null) ? base.GetControllerInstance(controllerType) : ServiceLocator.Current.GetInstance(controllerType) as IController;
        }
    }
}

We inheriting from the ASP.NET MVC default controller factory and overriding just a single method where we are using the ServiceLocator to create the requested controller. But before that we have to specify the ServiceLocator which IoC/DI container it should use, in this case it is Unity. In the global.asax:

IUnityContainer container = new UnityContainer();

UnityConfigurationSection configuration = (UnityConfigurationSection) ConfigurationManager.GetSection("unity");
configuration.Containers.Default.Configure(container);

ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(container));

We are creating an Unity Container and configuring it from the configuration, although you can use the nice fluent syntax of Unity instead of using configuration file. Next, we are creating the UnityAdapter by passing the container and instructing the ServiceLocator to use it. The configuration:

<configuration>
	<configSections>
		<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" requirePermission="false"/>
	</configSections>
	<appSettings/>
	<unity>
		<typeAliases>
			<typeAlias alias="PerWebRequest" type="UnityCommonServiceLocatorMVC.UnityPerWebRequestLifetimeManager, UnityCommonServiceLocatorMVC"/>
			<typeAlias alias="IDatabase" type="UnityCommonServiceLocatorMVC.IDatabase, UnityCommonServiceLocatorMVC"/>
			<typeAlias alias="Database" type="UnityCommonServiceLocatorMVC.InMemoryDatabase, UnityCommonServiceLocatorMVC"/>
			<typeAlias alias="ICategoryRepository" type="UnityCommonServiceLocatorMVC.ICategoryRepository, UnityCommonServiceLocatorMVC"/>
			<typeAlias alias="CategoryRepository" type="UnityCommonServiceLocatorMVC.CategoryRepository, UnityCommonServiceLocatorMVC"/>
			<typeAlias alias="IProductRepository" type="UnityCommonServiceLocatorMVC.IProductRepository, UnityCommonServiceLocatorMVC"/>
			<typeAlias alias="ProductRepository" type="UnityCommonServiceLocatorMVC.ProductRepository, UnityCommonServiceLocatorMVC"/>
			<typeAlias alias="HomeController" type="UnityCommonServiceLocatorMVC.HomeController, UnityCommonServiceLocatorMVC"/>
		</typeAliases>
		<containers>
			<container>
				<types>
					<type type="IDatabase" mapTo="Database">
						<lifetime type="PerWebRequest"/>
					</type>
					<type type="ICategoryRepository" mapTo="CategoryRepository">
						<lifetime type="PerWebRequest"/>
						<typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration">
							<constructor>
								<param name="database" parameterType="IDatabase">
									<dependency/>
								</param>
							</constructor>
						</typeConfig>
					</type>
					<type type="IProductRepository" mapTo="ProductRepository">
						<lifetime type="PerWebRequest"/>
						<typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration">
							<constructor>
								<param name="database" parameterType="IDatabase">
									<dependency/>
								</param>
							</constructor>
						</typeConfig>
					</type>
					<type type="HomeController">
						<typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices.Unity.Configuration">
							<constructor>
								<param name="categoryRepository" parameterType="ICategoryRepository">
									<dependency/>
								</param>
								<param name="productRepository" parameterType="IProductRepository">
									<dependency/>
								</param>
							</constructor>
						</typeConfig>
					</type>
				</types>
			</container>
		</containers>
	</unity>
	<system.web>
		<httpModules>
			<add name="UnityPerWebRequestLifetimeModule" type="UnityCommonServiceLocatorMVC.UnityPerWebRequestLifetimeModule, UnityCommonServiceLocatorMVC"/>
		</httpModules>
	</system.web>
	<system.webServer>
		<validation validateIntegratedModeConfiguration="false"/>
		<modules runAllManagedModulesForAllRequests="true">
			<remove name="UnityPerWebRequestLifetimeModule"/>
			<add name="UnityPerWebRequestLifetimeModule" type="UnityCommonServiceLocatorMVC.UnityPerWebRequestLifetimeModule, UnityCommonServiceLocatorMVC"/>
		</modules>
	</system.webServer>
</configuration>

First, we have added the types in typealias section so that we do not have to specify the complete type name later on. Next, IDatabase, ICategoryRepository and IProductRepository is mapped to its corresponding implementation and it will use the UnityPerWebRequestLifetimeManager that we have created earlier.  And at last we have added the UnityPerWebRequestLifetimeModule in the httpModules section so that the database which is disposable is disposed when the request ends.

You can find the complete solution in the following zip file.

UnityCommonServiceLocatorMVC.zip

Shout it

In the latest release of ASP.NET MVC, a new attribute ValidateInput is introduced which is same as Web Forms ValidateRequest page directive, certainly a good choice. Phil Haack blogged it, so as Steven Smith and also Nick Berarrdi. But it starts to break when you want to accept html tags from your user and using RenderAction of MVC Future to render different parts of your page. Consider the following screenshot DotNetShoutout story submit page:

Submit

  1. Rendered by Html.RenderAction<MembershipController>(c => c.Menu()) in Master Page.
  2. Rendered by Html.RenderAction<CategoryController>(c => c.Menu()) in Master Page.
  3. This is original request StoryController.Submit.
  4. Rendered by Html.RenderAction<TagController>(c => c.Tabs()) in Master Page.
  5. Rendered by Html.RenderAction<CategoryController>(c => c.RadioButtonList()) in Content Page.

The story description box will allow few known html tags and for this I have to decorate all of the above controllers method with ValidateInput(false) which is bit of annoying. I think the problem is mainly in MVC Future as it takes the same HttpContext but does not mark it as child/worker request (which I think it should) so when the ControllerActionInvoker.InvokeAction() comes into the action it does not takes the original attribute decoration into consideration. This is not a big deal for a small app like KiGG, but is definitely going to be if I am planning to develop DotNetNuke like CMS with ASP.NET MVC.

I think I need to take a look at the MVCContrib, how they have handled this situation with SubController.

Shout it

I just want share few tips with the DotNetShoutout users, maybe you have already discovered it, if you have not then it might be handy.

Feed

If you subscribe to our published or upcoming feed, you will notice that clicking on the link does not take you back to DotNetShoutout story page, instead it will take you directly to the original Story page, saving you from one unnecessary click.

Feed

With the story text you will also find the number of shout and a small page preview, if you want to shout the story click the shout it counter, it will take you to the DotNetShoutout page. And by the way you can subscribe to our Upcoming feed without worrying about spam, our system is quite equipped fighting with those spammers.

Sharing

Like the feed when you share a story, it will share the original url instead of the DotNetShoutout url. Currently we are only supporting MSDN Social(though I am not sure how many people are using it), delicious, facebook and twitter and for twitter we are even shrinking the original url to fit in 140 characters.

Share

And in Twitter:

Twitter

Shout it Badge

If you are blogging from Live write and putting the Shout it badge manually, please stop. We do a have plug-in for the latest Live Writer which autmattically inserts the counter in the footer of your post. Just visit the release section of codeplex and download the LiveWriterBinary.zip, it is just a simple dll (you can also get the source code version from the release page) which you have to copy in the plug-in folder of Live Writer (e.g . C:\Program Files\Windows Live\Writer\Plugins). Once you copied the dll, you find the plug-in name in Live Writer menu –> Tool->Options->Plug-ins, like the following:

LiveWriter-Options

Make sure it is enabled, you can also change the default colors from the options depending upon your blog theme. Now write your post and click publish, live writer might ask you do you want to execute this plug-in, click yes. The counter will appear at the bottom of your post like this post.

Enjoy!!!

Shout it

As mention in my previous post that I will be discussing the technical side of KiGG. So this is the beginning and it will be a multi-part series. I will try to put as much detail as possible, do let me know if I missed anything.

Just for a recap, KiGG is Web 2.0 style social news application where I am trying to exercise some of the best practice like TDD, DDD, SOLID etc with Microsoft supported tooling. If you want to see it in action just visit http://dotnetshoutout.com.

KiGG is already a fully functional application, but here I am starting from scratch, so the actual code might not look the same with following but it will show you how it is evolved as I am going to post more on it over the time.

Let us begin with the core functionalities:

  • User should be able to submit story.
    • User will select a category and can specify multiple tags when submitting story.
    • User should not be able to submit a story which already exists (based upon Url).
    • The system will ensure the story is a .NET related story, otherwise it will need moderator approval prior make it visible to everyone.
  • User should be able to promote story.
    • When the story has not been promoted by him/her.
    • When the story has not been marked as spam by him/her.
  • User should be able to demote story.
    • When the story has not been submitted by him/her.
    • When the story has been previously promoted by him/her.
  • User should be able to mark story as spam.
    • When the story has not been promoted by him/her.
    • When the story has not been marked as spam by him/her.
    • When story is not published.
  • User should be able to post comment.
    • The system will ensure the comment is not a spam.
  • User should be able to subscribe/unsubscribe comments of a Story.
  • User should be able to Tag story.
  • User should be able to view the original story by clicking the link and the count should be maintained.
  • User Score should be increased/decreased based upon the above actions.
  • User should be able to view the story list by published/upcoming/category/tag/user etc.
  • Moderator should be able to Edit Story.
  • Moderator should be able to Delete Story.
  • Moderator should be able to Confirm a Story/Comment as Spam.
  • Moderator should be able to Approve a Story as not spam which was previously marked as spam.
  • Admin should be able to Lock/Unlock a User.
  • Admin should be able to change role of a User.
  • Admin should be able to Publish stories at periodic interval.

With the above functionalities we can come up with an object model which consists classes like User, Story, Tag, Category, Vote, MarkAsSpam, Comment:

                                               Pre-DomainObjects

Now, lets refine this domain model with TDD (I will skip the red part of red-green-refactor to save space of this post, but you should always exercise the red part while developing in this way).

Promoting

When promoting the story we have to check two things, user has not previously promoted it and not marked it as spam. Lets write the tests:

[Fact]
public void HasPromoted_Should_Return_False_When_User_Has_Not_Previously_Promoted()
{
    var user = new User();

    Assert.False(_story.HasPromoted(user));
}

[Fact]
public void HasMarkedAsSpam_Should_Return_False_When_User_Has_Not_Previously_MarkedAsSpam()
{
    var user = new User();

    Assert.False(_story.HasMarkedAsSpam(user));
}

And in Story:

public bool HasPromoted(User byUser)
{
    return Votes.Any(v => v.ByUser.Id == byUser.Id);
}

public bool HasMarkedAsSpam(User byUser)
{
    return MarkAsSpams.Any(m => m.ByUser.Id == byUser.Id);
}

Now, create a new method that internally calls these two methods, so that client can only call this single method instead of calling those, test:

[Fact]
public void CanPromote_Should_Return_True_When_Story_Has_Not_Been_Promoted()
{
    var user = new User { Id = Guid.NewGuid() };

    Assert.True(_story.CanPromote(user));
}

[Fact]
public void CanPromote_Should_Return_True_When_Story_Has_Not_Been_MarkedAsSpam_By_The_User()
{
    var user = new User { Id = Guid.NewGuid() };

    Assert.True(_story.CanPromote(user));
}

And in Story:

public bool CanPromote(User byUser)
{
    return !HasPromoted(byUser) && !HasMarkedAsSpam(byUser);
}

Now the actual promote, note that we have introduced a new property  Timestamp in Vote, tests:

[Fact]
public void Promote_Should_Return_True_When_User_Can_Promote()
{
    var user = new User { Id = Guid.NewGuid() };

    Assert.True(_story.Promote(DateTime.UtcNow, user));
}

[Fact]
public void Promote_Should_Add_User_Vote_In_Votes()
{
    var user = new User { Id = Guid.NewGuid() };

    _story.Promote(DateTime.UtcNow, user);

    Assert.True(_story.Votes.Count(v => v.ByUser.Id == user.Id) > 0);
}

And in Story:

private readonly List<Vote> _votes = new List<Vote>();

public ICollection<Vote> Votes
{
    get
    {
        return _votes.AsReadOnly();
    }
}

public bool Promote(DateTime at, User byUser)
{
    if (CanPromote(byUser))
    {
        Vote vote = new Vote
                        {
                            ByUser = byUser,
                            ForStory = this,
                            Timestamp = at
                        };

        _votes.Add(vote);

        return true;
    }

    return false;
}

Notice that I have exposed the Votes as read only collection, though the ReadOnlyCollection<T> of ObjectModel namespace should be more appropriate, but I would like to stick with the interface instead of concrete class. So adding vote in Votes will raise exception, the client always have to use the Promote method.

Demoting

Demoting is similar to promoting, we have to check that the User has not submitted the story and the story has been previously promoted by the user(You can only demote if you previously promoted it), tests:

[Fact]
public void CanDemote_Should_Return_True_When_Story_Has_Been_Promoted_By_The_User()
{
    //Promote it first
    var user = new User { Id = Guid.NewGuid() };
    _story.Promote(DateTime.UtcNow, user);

    Assert.True(_story.CanDemote(user));
}

[Fact]
public void Demote_Should_Return_True_When_User_Can_Demote()
{
    //Promote it first
    var user = new User { Id = Guid.NewGuid() };
    _story.Promote(DateTime.UtcNow, user);

    Assert.True(_story.Demote(user));
}

[Fact]
public void Demote_Should_Remove_User_Vote_From_Votes()
{
    //Promote it first
    var user = new User { Id = Guid.NewGuid() };
    _story.Promote(DateTime.UtcNow, user);

    _story.Demote(user);

    Assert.True(_story.Votes.Count(v => v.ByUser.Id == user.Id) == 0);
}

And in Story:

public bool CanDemote(User byUser)
{
    return (PostedBy.Id != byUser.Id) && HasPromoted(byUser);
}

public bool Demote(User byUser)
{
    if (CanDemote(byUser))
    {
        Vote vote = _votes.Single(v => v.ByUser.Id == byUser.Id);

        _votes.Remove(vote);

        return true;
    }

    return false;
}

Marking as Spam

Marking as spam is also similar to story promoting, only a new checking that is if the story is published, so we will need a new property which will indicate whether the story is published, lets create a new property PublishedAt of nullable DateTime and create an extension method:

public static bool IsPublished(this Story story)
{
    return (story.PublishedAt.HasValue);
}

we also introduced a new property Timestamp for MarkAsSpam like vote, tests:

[Fact]
public void CanMarkAsSpam_Should_Return_True_When_Story_Has_Not_Been_Published()
{
    var user = new User { Id = Guid.NewGuid() };

    Assert.True(_story.CanMarkAsSpam(user));
}

[Fact]
public void CanMarkAsSpam_Should_Return_True_When_Story_Has_Not_Been_Promoted_By_The_User()
{
    var user = new User { Id = Guid.NewGuid() };

    Assert.True(_story.CanMarkAsSpam(user));
}

[Fact]
public void CanMarkAsSpam_Should_Return_True_When_Story_Has_Not_Been_MarkedAsSpam_By_The_User()
{
    var user = new User { Id = Guid.NewGuid() };

    Assert.True(_story.CanMarkAsSpam(user));
}

[Fact]
public void MarkAsSpam_Should_Return_True_When_User_Can_Mark_As_Spam()
{
    var user = new User { Id = Guid.NewGuid() };

    Assert.True(_story.MarkAsSpam(DateTime.UtcNow, user));
}

[Fact]
public void MarkAsSpam_Should_Add_User_Marking_In_MarkAsSpams()
{
    var user = new User { Id = Guid.NewGuid() };

    _story.MarkAsSpam(DateTime.UtcNow, user);

    Assert.True(_story.MarkAsSpams.Count(m => m.ByUser.Id == user.Id) > 0);
}

And in Story

private readonly List<MarkAsSpam> _markAsSpams = new List<MarkAsSpam>();

public ICollection<MarkAsSpam> MarkAsSpams
{
    get
    {
        return _markAsSpams.AsReadOnly();
    }
}

public bool CanMarkAsSpam(User byUser)
{
    return !this.IsPublished() && !HasPromoted(byUser) && !HasMarkedAsSpam(byUser);
}

public bool MarkAsSpam(DateTime at, User byUser)
{
    if (CanMarkAsSpam(byUser))
    {
        MarkAsSpam markAsSpam = new MarkAsSpam
                                    {
                                        ByUser = byUser,
                                        ForStory = this,
                                        Timestamp = at
                                    };

        _markAsSpams.Add(markAsSpam);

        return true;
    }

    return false;
}

Post Comment

For comment we have to introduce few new properties, Id, Content and CreatedAt. We will skip the spam checking part for the future post, tests:

[Fact]
public void PostComment_Should_Return_New_Comment()
{
    var user = new User{ Id = Guid.NewGuid() };

    var comment = _story.PostComment("This is a dummy content", DateTime.UtcNow, user);

    Assert.NotNull(comment);
}

[Fact]
public void PostComment_Should_Increase_Comments_Collection()
{
    var previousCount = _story.Comments.Count;

    var user = new User { Id = Guid.NewGuid() };

    _story.PostComment("This is a dummy content", DateTime.UtcNow, user);

    Assert.True(_story.Comments.Count > previousCount);
}

And in Story:

private readonly List<Comment> _comments = new List<Comment>();

public ICollection<Comment> Comments
{
    get
    {
        return _comments.AsReadOnly();
    }
}

public Comment PostComment(string content, DateTime at, User byUser)
{
    Comment comment = new Comment
                          {
                              Id = Guid.NewGuid(),
                              ByUser = byUser,
                              ForStory = this,
                              CreatedAt = at,
                              Content = content
                          };

    _comments.Add(comment);

    return comment;
}

Subscribing/Unsubscribing Comments

When subscribing comment we will check if the user has already subscribed, if not we will subscribe otherwise we will ignore it, tests:

[Fact]
public void ContainsCommentSubscriber_Should_Return_False_When_User_Does_Not_Exist_In_CommentSubscribers_Collection()
{
    var user = new User { Id = Guid.NewGuid() };

    Assert.False(_story.ContainsCommentSubscriber(user));
}

[Fact]
public void SubscribeComment_Should_Increase_CommentSubscribers_Collection()
{
    var user = new User { Id = Guid.NewGuid() };

    var previousCount = _story.CommentSubscribers.Count;

    _story.SubscribeComment(user);

    Assert.True(_story.CommentSubscribers.Count > previousCount);
}

[Fact]
public void SubscribeComment_Should_Not_Increase_CommentSubscribers_Collection_When_User_Already_Exists()
{
    var user = new User { Id = Guid.NewGuid() };

    _story.SubscribeComment(user);

    var previousCount = _story.CommentSubscribers.Count;

    _story.SubscribeComment(user);

    Assert.Equal(_story.CommentSubscribers.Count, previousCount);
}

[Fact]
public void UnsubscribeComment_Should_Decrease_CommentSubscribers_Collection()
{
    var user = new User { Id = Guid.NewGuid() };
    _story.SubscribeComment(user);

    var previousCount = _story.CommentSubscribers.Count;

    _story.UnSubscribeComment(user);

    Assert.True(_story.CommentSubscribers.Count < previousCount);
}

And in Story:

private readonly List<User> _commentSubscribers = new List<User>();

public ICollection<User> CommentSubscribers
{
    get
    {
        return _commentSubscribers.AsReadOnly();
    }
}

public bool ContainsCommentSubscriber(User byUser)
{
    return _commentSubscribers.Any(cs => cs.Id == byUser.Id);
}

public void SubscribeComment(User byUser)
{
    if (!ContainsCommentSubscriber(byUser))
    {
        _commentSubscribers.Add(byUser);
    }
}

public void UnSubscribeComment(User byUser)
{
    if (ContainsCommentSubscriber(byUser))
    {
        var s = _commentSubscribers.Single(cs => cs.Id == byUser.Id);
        _commentSubscribers.Remove(s);
    }
}

Viewing Story

We need the track how many times a story has been viewed, so let us introduce another property ViewCount, Test:

[Fact]
public void View_Should_Increase_ViewCount()
{
    var previousCount = _story.ViewCount;

    _story.View();

    Assert.True(_story.ViewCount > previousCount);
}

And in Story:

public int ViewCount
{
    get;
    private set;
}

public void View()
{
    ViewCount += 1;
}

Increasing/Decreasing User Score

User score should be increased/decreased based upon the action like submitting/promoting/demoting etc. So first let us create an Enum which will hold some predefined User Actions:

public enum UserAction
{
    None = 0,
    AccountActivated = 1,
    StorySubmitted = 2,
    StoryViewed = 3,
    StoryPromoted = 4,
    StoryCommented = 5,
    StoryMarkedAsSpam = 6,
    SpamStorySubmitted = 7
}

With increasing/decreasing user score we also need to have the support to know the current score and query score for a given time period, so lets add a new class which holds the date, score and user action when increasing/decreasing score, tests:

[Fact]
public void CurrentScore_Should_Be_Zero_When_User_Is_Created()
{
    Assert.Equal(0, _user.CurrentScore);
}

[Fact]
public void IncreaseScoreBy_Should_Increase_CurrentScore()
{
    decimal previousScore = _user.CurrentScore;

    _user.IncreaseScoreBy(5, UserAction.StorySubmitted);

    Assert.Equal(previousScore + 5, _user.CurrentScore);
}

[Fact]
public void DecreaseScoreBy_Should_Decrease_CurrentScore()
{
    decimal previousScore = _user.CurrentScore;

    _user.DecreaseScoreBy(50, UserAction.SpamStorySubmitted);

    Assert.Equal(previousScore - 50, _user.CurrentScore);
}

And in User:

public DateTime CreatedAt
{
    get;
    set;
}

public decimal CurrentScore
{
    get
    {
        return GetScoreBetween(CreatedAt, DateTime.UtcNow);
    }
}

private decimal GetScoreBetween(DateTime from, DateTime to)
{
    return _userScores.Where(us => us.Timestamp >= from && us.Timestamp <= to).Sum(us => us.Score);
}

public void IncreaseScoreBy(decimal score, UserAction reason)
{
    AddScore(score, reason);
}

public void DecreaseScoreBy(decimal score, UserAction reason)
{
    AddScore(-score, reason);
}

private void AddScore(decimal score, UserAction reason)
{
    _userScores.Add(
                        new UserScore
                        {
                            OfUser = this,
                            Reason = reason,
                            Score = score,
                            Timestamp = DateTime.UtcNow
                        });
}

Tag Story

When submitting story, user should be able to specify tags for story and later on can view the stories with that specified tags, so we need to associate tag with both User and Story. Since it is a common behavior, we can create an interface which both Story and User has to implement:

public interface ITagContainer
{
    ICollection<Tag> Tags
    {
        get;
    }

    void AddTag(Tag tag);

    void RemoveTag(Tag tag);

    void RemoveAllTags();

    bool ContainsTag(Tag tag);
}

Though composition is better than inheritance, but for the time being lets keep it this way. Since both is the same I am only showing the Story, tests:

[Fact]
public void Tags_Should_Be_Empty_When_Story_Is_Created()
{
    Assert.Empty(_story.Tags);
}

[Fact]
public void Contains_Should_Return_True_When_Tag_Exists_In_Tags()
{
    var tag = new Tag
                  {
                      Id = Guid.NewGuid(),
                      Name = "Dummy Tag"
                  };

    _story.AddTag(tag);

    Assert.True(_story.ContainsTag(new Tag { Name = "Dummy Tag" }));
}

[Fact]
public void AddTag_Should_Increase_Tags_Collection()
{
    int previousCount = _story.Tags.Count;

    var tag = new Tag
                  {
                      Id = Guid.NewGuid(),
                      Name = "Dummy Tag"
                  };

    _story.AddTag(tag);

    Assert.True(_story.Tags.Count > previousCount);
}

[Fact]
public void AddTag_Should_Not_Increase_Tags_Collection_For_Duplicate_Tag()
{
    var tag1 = new Tag
                   {
                       Id = Guid.NewGuid(),
                       Name = "Dummy Tag"
                   };

    _story.AddTag(tag1);

    int previousCount = _story.Tags.Count;

    var tag2 = new Tag
                   {
                       Id = Guid.NewGuid(),
                       Name = "Dummy Tag"
                   };

    _story.AddTag(tag2);

    Assert.Equal(previousCount, _story.Tags.Count);
}

[Fact]
public void RemoveTag_Should_Decrease_Tags_Collection()
{
    var tag = new Tag
                  {
                      Id = Guid.NewGuid(),
                      Name = "Dummy Tag"
                  };

    _story.AddTag(tag);

    int previousCount = _story.Tags.Count;

    _story.RemoveTag(new Tag { Name = "Dummy Tag" });

    Assert.True(_story.Tags.Count < previousCount);
}

[Fact]
public void RemoveAllTags_Should_Make_Tags_Collection_Empty()
{
    _story.AddTag(new Tag { Id = Guid.NewGuid(), Name = "Dummy Tag1" });
    _story.AddTag(new Tag { Id = Guid.NewGuid(), Name = "Dummy Tag2" });

    _story.RemoveAllTags();

    Assert.Empty(_story.Tags);
}

And in story:

private readonly List<Tag> _tags = new List<Tag>();

public ICollection<Tag> Tags
{
    get
    {
        return _tags.AsReadOnly();
    }
}

public void AddTag(Tag tag)
{
    if (!ContainsTag(tag))
    {
        _tags.Add(tag);
    }
}

public void RemoveTag(Tag tag)
{
    Tag sameNameTag = _tags.SingleOrDefault(t => t.Name == tag.Name);

    if (sameNameTag != null)
    {
        _tags.Remove(sameNameTag);
    }
}

public void RemoveAllTags()
{
    _tags.Clear();
}

public bool ContainsTag(Tag tag)
{
    return _tags.Any(t => t.Name == tag.Name);
}

Skipping few functionalities

We will skip story submit, edit, delete, confirm as spam, view story list for future post.

Changing Role of User

Admin should be able to change role of User. So we need to introduce another property in User. We will have some predefined role and the functionalities for each role should be static. First, let create an Enum

public enum Roles
{
    User = 0,
    Bot = 1,
    Moderator = 2,
    Administrator = 4
}

Next, the user will have a property Role and a method to change the role, we will skip the checking whether the caller is an admin for future when changing the role, test:

[Fact]
public void ChangeRole_Should_Update_Role()
{
    _user.ChangeRole(Roles.Administrator);

    Assert.Equal(Roles.Administrator, _user.Role);
}

and in User:

public Roles Role
{
    get;
    private set;
}

public void ChangeRole(Roles role)
{
    Role = role;
}

Lock/Unlock User

Admin should be able to lock/unlock user. It should be same as Role, we will again skip the admin calling checking for future, tests:

[Fact]
public void Lock_Should_Update_IsLocked()
{
    _user.Lock();

    Assert.True(_user.IsLocked);
}

[Fact]
public void Unlock_Should_Update_IsLocked()
{
    _user.Unlock();

    Assert.False(_user.IsLocked);
}

And in User:

public bool IsLocked
{
    get;
    private set;
}

public void Lock()
{
    IsLocked = true;
}

public void Unlock()
{
    IsLocked = false;
}

Approving Story

User can Mark story as spam if they think the story is not relevant, also our spam checkers can block a story from appearing if they detect it as spam. But the Moderator should be the final judge for confirming a story as spam, if the Moderator finds a story is not spam, s/he will approve the story which makes sure the story appearance.  For marking the Story as approved we will introduce a new Property ApprovedAt, same as Story published, tests:

[Fact]
public void Approve_Should_Update_ApprovedAt()
{
    var now = DateTime.UtcNow;

    _story.Approve(now);

    Assert.Equal(now, _story.ApprovedAt);
}

And in Story:

public DateTime? ApprovedAt
{
    get;
    private set;
}

public void Approve(DateTime at)
{
    ApprovedAt = at;
}

Note that we are again skipping the checking if the caller is Moderator in Approve method for future.

Publishing Story

Story publish is a process where popular stories appears in the front page, certainly there should be different strategies for calculating the popularity of story, which we will again it skip for future post, for the time being we will only focus once a story is qualified to publish how do we mark it. As mentioned in the Marking as Spam section that once a Story is published we will update its PublishedAt property, but updating only the PublishedAt does not serves our purpose as we also have to know the Rank to order it in the list. Tests:

[Fact]
public void Publish_Should_Update_PublishedAt()
{
    var now = DateTime.UtcNow;

    _story.Publish(now, 1);

    Assert.Equal(now, _story.PublishedAt);
}

[Fact]
public void Publish_Should_Update_Rank()
{
    _story.Publish(DateTime.UtcNow, 1);

    Assert.Equal(1, _story.Rank);
}

And in story:

public DateTime? PublishedAt
{
    get;
    private set;
}

public int? Rank
{
    get;
    private set;
}

public void Publish(DateTime at, int rank)
{
    PublishedAt = at;
    Rank = rank;
}

The following is final object model of this part that we have done so far:

DomainObjects

You can also download the complete code from the bottom of this post.

There are few points in the above which I like to highlight

 

  • Note that in above we are only focusing on the Domain Logic, we did not mentioned anything about database, UI or any other infrastructural stuffs.
  • Almost all of the code blocks are 3/4 lines long which is easy to understand and unit test.
  • There are certain functionalities in the above can be also achieved by using only properties instead of using method, for example in the User we can have IsLocked property with both getter and setter but instead we have created Lock, Unlock method and readonly property IsLocked which I think makes it more explicit and also expressive.

In the next post, we will check how to map this domain model into the database and create repository with LinqToSql with 100% code coverage.

Stay tuned!!!

Download: Source Code

Shout it

Just to inform you that my blog’s feedburner url has been changed.

Current : http://feeds2.feedburner.com/kazimanzurrashid

Old: http://feeds.feedburner.com/manzurrashid

Sorry readers for this inconvenience.

Shout it

It seems a lot of people is having some hard time configuring KiGG in their box. My fellow team member Iftekhar Ahmed has created a short screen cast, I hope it will solve this issue.

The environment:

  1. ASP.NET MVC RC 1 Installed.
  2. IIS7
  3. SQL Server 2008 Express
  4. Visual Studio Enterprise Architect
  5. KiGG v 2.1
  6. We already created PageGlimpse and reCaptcha.net account.

I hope for other platform like VWD/VS Web Server, it will be very much same.

 



KiGG Configure v2.1 from Iftekhar Ahmed on Vimeo.
Shout it
More Posts