
seen from United States
seen from Mexico

seen from United Kingdom

seen from Romania
seen from United States
seen from Australia

seen from United Kingdom

seen from Canada
seen from Germany

seen from Singapore

seen from United Kingdom
seen from Yemen
seen from Singapore

seen from Malaysia
seen from Singapore

seen from Türkiye

seen from Singapore
seen from China

seen from United States
seen from Brazil
Refactoring: implementing Unity and EventHandling
I've been maintaining a fairly complex website that I built in 2005. Between 2005 and now there have been quite some code improvements (packaging and minification, .net 2.0, caching). But last couple of days I made a big step forward: implementing and IoC container.
Why? Inversion of Control states that different services with different purposes should be separated and should only communicate with eachother's interface. This is a huge benefit for unit-testability; you can easily test the interface methods of a service (using Moq for example).
But it also helps to make a better choice of which service should hold a specific function and what the dependencies are. Take this code example:
public void DeleteDocument(int id) { Sql.ExecuteNonQuery("DELETE FROM docs WHERE id=?", id); Sql.ExecuteNonQuery("DELETE FROM history WHERE docid=?", id); Sql.ExecuteNonQuery("DELETE FROM users WHERE docid=?", id); Sql.ExecuteNonQuery("DELETE FROM linksindex WHERE from_id=? OR to_id=?", id, id); }
With Unity, I've changed it to this:
public void DeleteDocument(int id) { Sql.ExecuteNonQuery("DELETE FROM docs WHERE id=?", id); if (OnDocumentDeleted != null) OnDocumentDeleted(this, new DocumentDeletedEventArgs(id)); }
And now the HistoryService, the UsersService and the LinksService can each attach to the OnDocumentDeleted function and execute their own deletion code. Maybe they want to clear some caches, or do some other stuff when a document gets deleted. Each service maintains it's own storage and functionality.
Implementing Unity First of all I updated the project from .net 2.0 to .net 4.0. This'll mean upgrading the live server, but other than that nothing broke :)
Next up was installing the Unity NuGet package, and installing a ServiceManager class that contains the UnityContainer. The following code is in a separate ClassLibrary, so it can also be called from possible command-line projects.
public class ServiceManager { private static ServiceManager _servicemanager; private IUnityContainer _container; ServiceManager() { _container = new UnityContainer(); } public static ServiceManager Instance { get { if (_servicemanager == null) _servicemanager = new ServiceManager(); return _servicemanager; } } public IUnityContainer Container { get { return _container; } } }
So now anywhere in the project you can get to the UnityContainer by calling ServiceManager.Instance.Container. And we'll start out by registering all our newly built services in global.asax:
void Application_Start(object sender, EventArgs e) { log4net.Config.XmlConfigurator.Configure(); AppInitializer.Init(); }
public static class AppInitializer { public static void Init() { ServiceManager.Instance.Container .RegisterType<Settings>(new ContainerControlledLifetimeManager()) .RegisterType<ICacheService, HttpRuntimeCacheService>(new ContainerControlledLifetimeManager()) .RegisterType<HistoryService>(new ContainerControlledLifetimeManager()); ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(ServiceManager.DefaultContainer)); } }
This registers three services as singleton's (because the container is a singleton, the services themselves are also singletons). Now I can have Unity inject the Settings into the HistoryService:
public HistoryService(Settings settings) { this._settings = settings; }
Voila! This way I've created a DatabaseService, SearchService, IndexService, SessionService, etc.
Event handlers This got me a step further, but it wasn't done yet. At some point, the ListService needed to save a document, using the DatabaseService. But OnSave of a document, the ListService needed to be updated again. Circular dependency! StackOverflowException all over the place!
That's when I started using Event handling to fix these last dependencies. On the DatabaseService I created different EventHandlers:
public event EventHandler<DocumentDeletedEventArgs> OnDocumentDeleted; public event EventHandler<DocumentStoredEventArgs> OnDocumentStored;
public void DeleteDocument(int id) { Sql.ExecuteNonQuery("DELETE FROM docs WHERE id=?", id); if (OnDocumentDeleted != null) OnDocumentDeleted(this, new DocumentDeletedEventArgs(id)); }
And the HistoryService registered itself to handle a DocumentStored event:
public HistoryService(DatabaseService db) { this.Db = db; Db.OnDocumentStored += new EventHandler<DocumentStoredEventArgs>(Db_OnDocumentStored); }
Now there's on twitch: the HistoryService constructor only gets called when the historyservice is resolved using UnityContainer. So at first I tried to store a document and the historyservice didn't kick in. That's why I resolved every service on Application_Start:
public static class AppInitializer { public static void Init() { ServiceManager.Instance.Container .RegisterType<Settings>(new ContainerControlledLifetimeManager()) .RegisterType<ICacheService, HttpRuntimeCacheService>(new ContainerControlledLifetimeManager()) .RegisterType<HistoryService>(new ContainerControlledLifetimeManager()); ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(ServiceManager.DefaultContainer)); ServiceManager.DefaultContainer.Resolve<DatabaseService>(); ServiceManager.DefaultContainer.Resolve<HistoryService>(); } }
Future There's a lot of refactoring I want to do. There's too much dependencies (the function that gets one document from the database is 40 lines long and has a lot of side-effects). Also, there is a lot of XML being thrown around between services, which I want to replace with typed models.
These refactorings are now possible because I can unittest a part of the code before I change it, and then see if the code I wrote actually works :)