Today i want to show you my approach to resolve the requested entity framework object context at runtime. Starting point:
- First, i want to have just one implementation of an IUnitOfWork to resolve any kind of repository like Repository<Order>
- Second, my unit of work should autodiscover the proper context for my requested type of domain model as i have different databases and object contexts. For example, Order is a domain object of my domain model (namespace Domain.ComWork) according to the context ‘comwork’, which is mapping to the database ‘comwork’. There’s another domain object like Customer in my domain model (namespace Domain.MyHeco) according to the context ‘myheco’, which is mapping to the database ‘myheco’.
So how can i achieve this? I will use an IoC container like Castle.Windsor. You can add it to your Visual Studio project by the nuget command “install-package Castle.Windsor”. I want to mention that this scenario needs a little bit of advanced IoC techniques because the dependent component has to be resolved at runtime. So you’re not able to register a dedicated component for an interface. Instead you’ve got to use a facility. Allright, so we tell the container that we will register a facility:
1: container.AddFacility<TypedFactoryFacility>();
In line 1 we tell the container that it has to load the necessary infrastructure for facilities. Let’s take a look at the registration process. Here’s my method that will return me an IEnumerable of IRegistration.
1: static IEnumerable<IRegistration> Components()
2: {
3: yield return Component
4: .For<IContextFactory>()
5: .AsFactory(c => c.SelectedWith<ContextFactoryHandlerSelector>());
6: yield return Component
7: .For<ContextFactoryHandlerSelector>();
8: yield return Component
9: .For<IUnitOfWork>()
10: .ImplementedBy<UnitOfWork>();
11: yield return Component
12: .For<ComWorkContext>()
13: .Named("comworkcontext");
14: yield return Component
15: .For<MyHecoContext>()
16: .Named("myhecocontext");
17: }
Here’s the good news: Castle Windsor has an out of the box feature that creates you the needed factory. In other words: The container creates automatically the factory. You’re code is unaware of the container. Take a look at this article. If you want to use this you’ve have to follow some conventions. In my approach i decided to develop my own implementation to follow my own convetions. Take a look at line 5. With “.AsFactory” i tell the container that i don’t register a concrete implementation for that service (IContextFactory). If i would follow the conventions of Castle.Windsor calling AsFactory() would be enough.
Here’s the code for my own implementation of a TypedFactoryComponentSelector (this would be auto generated by using just AsFactory without parameters):
1: public class ContextFactoryHandlerSelector : ITypedFactoryComponentSelector
2: {
3: public TypedFactoryComponent SelectComponent(MethodInfo method,
4: Type type, object[] arguments)
5: {
6: var requestFor = method.GetGenericArguments().First();
7:
8: var @namespace = string.Format("{0}context",
9: requestFor.Namespace.ToLower()
10: .Replace("domain.",""));
11:
12: return new TypedFactoryComponent(@namespace, method.ReturnType, null);
13: }
14: }
My convention is pretty simple: The name of my object context is the same as the namespace of my model without the string “domain.” and with “context” appended.
Sample: Full name of my domain object: “domain.comwork.order”
- use just the namespace –> domain.comwork
- replace domain –> comwork
- add context –> comworkcontext
I have two kind of domain models (domain.comwork and domain.myheco) so i need two contexts (comworkcontext and myhecocontext). Every context is registered:
1: yield return Component
2: .For<ComWorkContext>()
3: .Named("comworkcontext");
4: yield return Component
5: .For<MyHecoContext>()
6: .Named("myhecocontext");
Now we’ve got it: We have a facility that invokes a call to my self implemented factory if a service (defined in the interface IContextFactory) is requested. The factory returns the name of the needed context at runtime. My Interface looks like this:
1: public interface IContextFactory
2: {
3: ObjectContext GetContextFor<T>();
4: }
And here’s the generic part. If i call GetContextFor<domain.comwork.order> i will get my comworkcontext. If i call GetContextFor<domain.myheco.customer> i will get my myhecocontext.
The last step is to inject the factory in my UnitOfWork. Nothing easier than that:
1: public class UnitOfWork : IUnitOfWork
2: {
3: private readonly IContextFactory _factory;
4:
5: public UnitOfWork(IContextFactory factory)
6: {
7: _factory = factory;
8: }
9:
10: //here you would return an IRepository<T>
11: //this is just a sample code
12: public void GetRepository<T>() where T : class
13: {
14: Console.WriteLine(_factory.GetContextFor<T>().GetContextName());
15: }
16: }
And how can i resolve my concrete UnitOfWork in my program?
1: static class Program
2: {
3: static void Main(string[] args)
4: {
5: using (var container = new WindsorContainer())
6: {
7: container.AddFacility<TypedFactoryFacility>();
8: container.Register(Components().ToArray());
9:
10: container.Resolve<IUnitOfWork>()
.GetRepository<Domain.ComWork.Foo>();
11: container.Resolve<IUnitOfWork>()
.GetRepository<Domain.MyHeco.Bar>();
12: }
13: }
14:
15: static IEnumerable<IRegistration> Components()
16: {
17: //Registrations...
18: }
19: }