Introduction:
In this post, we will see how we can inject WCF service dependency in the controller in asp.net mvc.
Background:
When working with web applications, normally we have persistent storage of data like Database, xml, Files , NoSQL , and we have data access in place in our controller actions to do operations on that data. Somtimes we fetch it for diaplying, saving new data, or updating existing recrods.
Nowadays service oriented applications are very common and there is trend of making service oriented applications so that we can add or remove faetures from applications easily or alter implementation without making things mess.
WCF is one of the technologies used which acts as a communication bridge between client application and persistent storage or database. We will discuss next with code sample how it is done nomally.
Ordinary Approach:
Normally, we create a WCF service project, Create our Service Contratcs in form of interfaces which tells what i take input and what i would return to callee and then others can implement those service contracts to tell how to do that. Here is an example Service Contract :
[ServiceContract]
public interface IUserService
{
[OperationContract]
IList<user> GetUsers();
[OperationContract]
User RegisterUser(User user);
[OperationContract]
User Login(string id,string password);
[OperationContract]
bool UserNameExists(string username, string email);
}
and we then provide the implementation of contract:
public class UserService : IUserService
{
private IConnectionFactory connectionFactory;
public IList<user> GetUsers()
{
connectionFactory = ConnectionHelper.GetConnection();
var context = new DbContext(connectionFactory);
var userRep = new UserRepository(context);
return userRep.GetUsers();
}
public User RegisterUser(User user)
{
connectionFactory = ConnectionHelper.GetConnection();
var context = new DbContext(connectionFactory);
var userRep = new UserRepository(context);
return userRep.CreateUser(user);
}
public User Login(string id, string password)
{
connectionFactory = ConnectionHelper.GetConnection();
var context = new DbContext(connectionFactory);
var userRep = new UserRepository(context);
return userRep.LoginUser(id,password);
}
public bool UserNameExists(string username,string email)
{
connectionFactory = ConnectionHelper.GetConnection();
var context = new DbContext(connectionFactory);
var userRep = new UserRepository(context);
var user = userRep.GetUserByUsernameOrEmail(username,email);
return !(user != null && user.UserID > 0);
}
}
Now client application (in our case mvc application) would add Service Reference in the project and call the Service as shown below:
public class AccountController : Controller
{
[HttpPost]
[AllowAnonymous]
public ActionResult SignUp(RegisterViewModel registerVM)
{
if (ModelState.IsValid)
{
UserService authenticateService = new UserService(); // this is bad design high coupling of components
var result = authenticateService.RegisterUser(registerVM);
RegisterViewModel vm = result;
return View(vm);
}
return View(registerVM);
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult SignIn(LoginViewModel loginVm)
{
if (ModelState.IsValid)
{
UserService authenticateService = new UserService(); // this is bad design high coupling of components
var user = authenticateService.Login(loginVm.UserName, loginVm.Password);
if (user == null)
ModelState.AddModelError("", "The user name or password provided is incorrect.");
Session["User"] = user;
return RedirectToAction("Index", "Home");
}
return View(loginVm);
}
}
What's the Problem:
If you note the above two action methods, they look fine but there is one line which is a bad design approach and voild Single Responsibility Principle and it also create high coupling between our controller and Service implementation, in future if we have to use some different implementation of
IUserService , let's say new implemention which uses Entity Framework or some other Data Access Technique like MongoDB, Azure or Rest Services with JSON, we will have to change our all actions to use different class, which is not very pleasant.
Dependency Injection :
Here comes the role of
dependency injection is a technique / design pattern which we will be using here. As
IUserService a dependency of our Controller which is needed for the
AccountController to do the job successfully.
As it name reflects it is used for injecting dependencies of an implementation, there can be many dependencies or can be one only depends on the scenario, but in our this example we have one dependency of our Controller which is IUserService.
Castle Windsor to Help Here:
Castle Windsor is a
Inversion of Control container, it can instantiate the dependencies right at the place where we need them. Dependency Injection and Inversion of Control are mostly used together, when we want dependencies to be injected, we have to use Inversion of Control as well.
For this, first of all we will have to add the library in out project from Nuget or we can download assemblies and add references to assemblies explictly. After installing the library we can see reference to Castle.Core and Castle.Windsor in References of project:
Now first step is to implement the installer for CastleWindsor in which we will be telling the dependencies which will be injected by container on runtime when needed.
public class WindsorInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
// registering all Controllers of the Assembly
container.Register(Classes.FromThisAssembly()
.BasedOn<icontroller>()
.LifestyleTransient());
// registeting our WcfInterceptor to intercept all WCF Service calls
container.Register(Component
.For<iinterceptor>()
.ImplementedBy<wcfinterceptor>()
.Named("wcf"));
// Registering IUserService
container.Register(Types
.FromAssemblyContaining<iuserservice*gt;()
.Where(x => x.IsInterface)
.LifestyleTransient()
.Configure(x =>
{ var res = x.Interceptors(InterceptorReference.ForKey("wcf")).Anywhere; }));
// Registering implementation for IClientFactory which is dependency of WCFInterceptor implementation
container.Register(Component
.For<iclientfactory>()
.ImplementedBy<wcfclientfactory>());
}
Following is the implementation of registered dependencies above. First of all here is implementation for
WcfInterceptor:
public class WcfInterceptor : IInterceptor
{
public IClientFactory ClientFactory { get; set; }
public void Intercept(IInvocation invocation)
{
var clientProvider = ClientFactory.GetClientProvider(invocation.Method.DeclaringType);
try
{
clientProvider.Open();
invocation.ReturnValue = CallClientProviderMethod(invocation, clientProvider);
}
finally
{
clientProvider.Close();
}
}
private object CallClientProviderMethod(IInvocation invocation, IClientProvider clientProvider)
{
var proxy = clientProvider.GetProxy();
return invocation.Method.Invoke(proxy, invocation.Arguments);
}
}
You can see the property ClientFactory of type
IClientFactory which is a dependency as it is being used in Intercept method, and Intercept will get called when from client side we will exceute any method of WCF Service e.g :
RegisterUser() method of SignUp action.
Here is the implementation of WcfClientFactory:
public class WcfClientFactory : IClientFactory
{
public IClientProvider GetClientProvider(Type type)
{
var closedType = typeof(WcfClientProvider<>).MakeGenericType(type);
return (IClientProvider)Activator.CreateInstance(closedType);
}
}
which just returns instance of WcfClientProvider<T>, the implementation of WcfClientprovide<T> is:
public class WcfClientProvider<t> : IClientProvider
{
private ChannelFactory<t> factory;
public WcfClientProvider()
{
factory = new ChannelFactory<t>(string.Empty);
}
public object GetProxy()
{
return factory.CreateChannel();
}
public void Open()
{
if (this.factory.State != CommunicationState.Opened)
{
factory.Open();
}
}
public void Close()
{
factory.Close();
}
}
Now
we have to define Controller Factory as well, because we have Registered
IController in container we now will have to provide our own implementation of Controller Factory whose purpose is to create controller instance passing the instantiated dependencies of it.
Now the last step is to install Castle Windsor on Application start and instantiation of our Controller Facotry in
Global.asax.
This is how your Global.asax code should look like:
public class MvcApplication : System.Web.HttpApplication
{
private static IWindsorContainer container;
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BootstrapContainer();
}
private static void BootstrapContainer()
{
container = new WindsorContainer()
.Install(FromAssembly.This());
var controllerFactory = new WindsorControllerFactory(container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
}
}
Now we will have to change our AccountController code to add a contructor which takes
IUserService instance as parameter (dependency) which will be instantiated by our Ioc Container.
Now our controller will look like :
public class AccountController : Controller
{
private IUserService authenticateService;
public AccountController(IUserService authenticateService)
{
this.authenticateService = authenticateService;
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult SignIn(LoginViewModel loginVm)
{
if (ModelState.IsValid)
{
var user = authenticateService.Login(loginVm.UserName, loginVm.Password);
if (user == null)
ModelState.AddModelError("", "The user name or password provided is incorrect.");
Session["User"] = user;
return RedirectToAction("Index", "Home");
}
return View(loginVm);
}
[HttpPost]
[AllowAnonymous]
public ActionResult SignUp(RegisterViewModel registerVM)
{
if (ModelState.IsValid)
{
var result = authenticateService.RegisterUser(registerVM);
RegisterViewModel vm = result;
return View(vm);
}
return View(registerVM);
}
Now our code is much better, it is following single responsibility principle and it is component based, we can also write Unit Tests for our actions as our all dependencies are to be provided via constructor, so unit tests are easy to write and run.