Policy Injection Intercept not working for WebAPI Controllers - c#

I'm implementing Policy injection with web api and for DI we are using custom DependancyResolver. I have used InterfaceIntercept approach to implement policy injection. It is working fine in case of Classes(custom created classes) but Policy Injection is not being fired in case of ApiController.
To call Policy Injection with APIController, I have created an interface & implemented that with the controller. Sharing code below :
Also I would need to call policy with MessageHandlers as well.
Policy Injection code :
public class LogExceptionsCallHandler : ICallHandler
{
public IMethodReturn Invoke(IMethodInvocation input,
GetNextHandlerDelegate getNext)
{
IApplicationLogger logger = new ApplicationLogger();
logger.Log(LoggingLevel.TRACE, "Entering " + input.MethodBase.DeclaringType.FullName + "." + input.MethodBase.Name);
//// Perform the operation
var methodReturn = getNext().Invoke(input, getNext);
//// Method failed, go ahead
if (methodReturn.Exception != null)
return methodReturn;
//// If the result is negative, then throw an exception
logger.Log(LoggingLevel.TRACE, "Ending " + input.MethodBase.DeclaringType.FullName + "." + input.MethodBase.Name);
return methodReturn;
}
public int Order { get; set; }
}
Attribute code
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Method)]
public class LogExceptionsAttribute : HandlerAttribute
{
public LogExceptionsAttribute()
{
}
public HandleLogging HandleLogging { get; set; }
public int RetryCount { get; set; }
public override ICallHandler CreateHandler(Microsoft.Practices.Unity.IUnityContainer container)
{
return new LogExceptionsCallHandler();
}
}
Interface code :This interface is being implemented by the ApiController
[LogExceptions]
public interface IRequestExecutionController : IHttpController
{
[LogExceptions]
HttpResponseMessage ExecuteRequest();
}
IRequestExecutionController interface is being implemented by the RequestExecutionController.
Registering type with unity :
container.RegisterType<IDDNPublicAPI.PassThrough.IRequestExecutionController, RequestExecutionController>("requestexecution");
Registering Intercept
container.Configure<Interception>().SetInterceptorFor<IDDNPublicAPI.PassThrough.IRequestExecutionController>(new InterfaceInterceptor());
As we have unity to resolve dependency, So we have created a controller factory class to handle Controller instance creation.
public class UnityControllerFactory : IHttpControllerActivator
{
private IUnityContainer container;
private readonly IControllerFactory innerFactory;
public UnityControllerFactory(IUnityContainer container)
: this(container, new DefaultControllerFactory())
{ }
public UnityControllerFactory(IUnityContainer container, IControllerFactory innerFactory)
{`enter code here`
this.container = container;
this.innerFactory = innerFactory;
}enter code here
public IHttpController Create(HttpRequestMessa`enter code here`ge request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
var controller = (IHttpController)this.container.Resolve(controllerType, controllerDescriptor.ControllerName.ToLower());
return controller;
}
}
And we have register this Controller factory in global config file. Same process is working for other classes but not working for the apicontroller.
Can any one suggest something on this ?

Web API Controllers are not intercept-able. But you can get the same result using a filter.
Here is a good post showing how you can do the logging in your controller using filters:
http://damienbod.wordpress.com/2013/09/15/aop-with-asp-net-web-api/
You can still use your logging interceptor to log any of your backend code.

Related

How can I use Dependency Injection in a .Net Core ActionFilterAttribute?

AuthenticationRequiredAttribute Class
public class AuthenticationRequiredAttribute : ActionFilterAttribute
{
ILoginTokenKeyApi _loginTokenKeyApi;
IMemoryCache _memoryCache;
public AuthenticationRequiredAttribute(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
_loginTokenKeyApi = new LoginTokenKeyController(new UnitOfWork());
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var memory = _memoryCache.Get(Constants.KEYNAME_FOR_AUTHENTICATED_PAGES);
string requestedPath = filterContext.HttpContext.Request.Path;
string tokenKey = filterContext.HttpContext.Session.GetString("TokenKey")?.ToString();
bool? isLoggedIn = _loginTokenKeyApi.IsLoggedInByTokenKey(tokenKey).Data;
if (isLoggedIn == null ||
!((bool)isLoggedIn) ||
!Constants.AUTHENTICATED_PAGES_FOR_NORMAL_USERS.Contains(requestedPath))
{
filterContext.Result = new JsonResult(new { HttpStatusCode.Unauthorized });
}
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
}
}
HomeController
public class HomeController : Controller
{
IUserApi _userApi;
ILoginTokenKeyApi _loginTokenKey;
IMemoryCache _memoryCache;
public HomeController(IUserApi userApi, ILoginTokenKeyApi loginTokenKey, IMemoryCache memoryCache)
{
_loginTokenKey = loginTokenKey;
_userApi = userApi;
_memoryCache = memoryCache;
}
[AuthenticationRequired] // There is AN ERROR !!
public IActionResult Example()
{
return View();
}
}
ERROR :
Error CS7036 There is no argument given that corresponds to the
required formal parameter 'memoryCache' of
'AuthenticationRequiredAttribute.AuthenticationRequiredAttribute(IMemoryCache)' Project.Ground.WebUI
My problem is actually : I cant use dependency injection in attribute classes.
I want to use that attribute without any parameter. Is there any solution to solve it? I use dependency injection but it cant be used for attributes. How I can use it?
As per the documentation, you have a few options here:
If your filters have dependencies that you need to access from DI, there are several supported approaches. You can apply your filter to a class or action method using one of the following:
ServiceFilterAttribute
TypeFilterAttribute
IFilterFactory implemented on your attribute
ServiceFilter or TypeFilter attributes
If you just want to get this working quickly, you can just use one of the first two options to apply your filter to a controller or a controller action. When doing this, your filter does not need to be an attribute itself:
[TypeFilter(typeof(ExampleActionFilter))]
public IActionResult Example()
=> View();
The ExampleActionFilter can then just implement e.g. IAsyncActionFilter and you can directly depend on things using constructor injection:
public class ExampleActionFilter : IAsyncActionFilter
{
private readonly IMemoryCache _memoryCache;
public ExampleActionFilter(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{ … }
}
You can also use the [ServiceFilter] attribute instead to get the same effect but then you will also need to register your ExampleActionFilter with the dependency injection container in your Startup.
Filter factory
If you need more flexibility, you can implement your own filter factory. This allows you to write the factory code to create the actual filter instance yourself. A possible implementation for the above ExampleActionFilter could look like this:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ExampleActionFilterAttribute : Attribute, IFilterFactory
{
public bool IsReusable => false;
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
return serviceProvider.GetService<ExampleActionFilter>();
}
}
You can then use that [ExampleActionFilter] attribute to make the MVC framework create an instance of the ExampleActionFilter for you, using the DI container.
Note that this implementation is basically the same thing that ServiceFilterAttribute does. It’s just that implementing it yourself avoids having to use the ServiceFilterAttribute directly and allows you to have your own attribute.
Using service locator
Finally, there is another quick option that allows you to avoid constructor injection completely. This uses the service locator pattern to resolve services dynamically when your filter actually runs. So instead of injecting the dependency and using it directly, you retrieve it explicitly from the context:
public class ExampleActionFilter : ActionFilterAttribute
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var memoryCache = context.HttpContext.RequestServices.GetService<IMemoryCache>();
// …
}
}
Instead of resolving at construction, ActionExecutingContext.HttpContext.RequestServices should give you a reference to the request's service container at the time of the request.
So:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var svc = filterContext.HttpContext.RequestServices;
var memCache = svc.GetService<IMemoryCache>();
//..etc
For .Net Core 5, Below syntax worked for me.
IAppUserService _appUserService = (IAppUserService)context.HttpContext.RequestServices.GetService(typeof(IAppUserService));

Set the type of registered Service during runtime within an action filter/message handler

public class ActionFilterVersionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.Request.Headers.Any(x => x.Key == "SetInternalVersion"))
{
// determine somehow that the **InternalSystem implementation** should be resolved when the controller class is instantiated with the **ISystem constructor** parameter
}
else
{
// determine somehow that the **ExternalSystem implementation** should be resolved when the controller class is instantiated with the **ISystem constructor** parameter
}
base.OnActionExecuting(actionContext);
}
}
I have ExternalSystem/InternalSystem with the ISystem interface.
How can I tell autofac to inject the ExternalSystem or InternalSystem into the instantiated controller as ISystem instance depending on the string value I pass in the ActionFilter or maybe message handler.
I know I can do stuff like:
builder.RegisterType<InternalSystem>().As<ISystem>().Keyed<ISystem>("Internal");
where I can use a func<string,ISystem> factory to resolve the class during runtime but this is not what I want to do.
Actually I need to register the ISystem within the the action filter, but then I would need somehow to pass the container into the filter, but that is not what I want...and prolly its also not possible.
// Action: returns external or internal value
public string Get()
{
return resolvedISystem.Get();
}
Of course I could resolve the ISystem depending on the func factory within each single action or put behavior into a base controller where I check for the header, but I really would prefer the action filter as it can be just globally registerd ONE time, but for each new controller I have to subclass the base controller.
Base controller sample with pseudo code , because the base.Request is null which needs another workaround/fix...
public class BaseController : ApiController
{
public BaseController(Func<string, ISystem> dataServiceFactory)
{
string system = base.Request.Headers.Any(x => x.Key == "SetInternalVersion") ? "internal" : "external";
System = dataServiceFactory(system);
}
public ISystem System { get; set; }
}
UPDATING the container is also marked as OBSOLETE by the Autofac author.
Thus I do not want to add registrations in my filter/handler and update/build the container again.
I think you should not use ActionFilter at all. You have a controller dependency which should be resolved properly based on the information coming from request. Here is a possible solution. You can use a static HttpContext.Current property in order to extract request header.
System classes:
public interface ISystem { }
public class ExternalSystem : ISystem { }
public class InternalSystem : ISystem { }
SystemKeyProvider:
public enum SystemKey
{
External,
Internal
}
public interface ISystemKeyProvider
{
SystemKey GetSystemKey();
}
public class SystemKeyProvider : ISystemKeyProvider
{
private const string HeaderKey = "SetInternalVersion";
private readonly HttpRequest _request;
public SystemKeyProvider(HttpRequest request)
{
_request = request;
}
public SystemKey GetSystemKey()
{
return (_request.Headers[HeaderKey] != null) ?
SystemKey.Internal :
SystemKey.External;
}
}
Controller constructor: ValuesController(ISystem system)
Autofac container registration:
var builder = new ContainerBuilder();
builder.Register(c => HttpContext.Current.Request).As<HttpRequest>().InstancePerRequest();
builder.RegisterType<SystemKeyProvider>().AsImplementedInterfaces();
// service registration
builder.RegisterType<ExternalSystem>().Keyed<ISystem>(SystemKey.External);
builder.RegisterType<InternalSystem>().Keyed<ISystem>(SystemKey.Internal);
builder.Register(c =>
c.ResolveKeyed<ISystem>(c.Resolve<ISystemKeyProvider>().GetSystemKey()))
.As<ISystem>();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
GlobalConfiguration.Configuration.DependencyResolver =
new AutofacWebApiDependencyResolver(builder.Build());
In this solution I created a SystemKeyProvider wrapper class which is responsible for providing appropriate key in order to resolve ISystem.
Demo:
When no SetInternalSystem header is present.
Then the dependency is resolved as ExternalSystem.
When SetInternalSystem header is present.
Then the dependency is resolved as InternalSystem.

How can I pass a mock object when I am doing setter injection in ASP.NET MVC Controller

Say, I have the below Controller
public class UsersController : Controller
{
private IUsersRepository UsersRepository { get; }
public UsersController()
{
UsersRepository = DependencyResolver.Current.GetService(typeof(IUsersRepository)) as IUsersRepository;
}
public ActionResult Index ()
{
MyUserDefinedModel data = UsersRepository.MyRepository();
return View(data);
}
}
Now I want to mock the IUsersRepository and pass it to the controller in my test script.
Below my test code
public class UsersListTest
{
private UsersController usersController = new Mock<IUsersRepository>();
private Mock<IUsersRepository> usersRepository = new UsersController();
[TestMethod]
public void TestMethod1()
{
//usersRepository.Setup(x => x.Get()).Returns(users);
}
}
As because private IUsersRepository UsersRepository { get; } private, I'm not able to pass the mock of IUsersRepository.
What would be the good idea to write unit test and mock in such case.
The reason that you have trouble with testing is because your Controller class uses the Service Locator anti-pattern. A Service Locator is a either a global instance (the DependencyResolver.Current) or an abstraction that allows resolving dependencies at runtime. One of the many downsides of the Service Locator is the problems it causes with testing.
You should move away from the Service Locator pattern and use dependency injection instead, favorably constructor injection. Your application components should have a single public constructor and those constructors should do nothing more than storing the incoming dependencies. This will result in the following UsersController implementation:
public class UsersController : Controller
{
private IUsersRepository usersRepository;
public UsersController(IUsersRepository usersRepository)
{
this.usersRepository = usersRepository;
}
public ActionResult Index()
{
return View(this.usersRepository.MyRepository());
}
}
With this in place, unit testing became trivial:
public class UsersControllerTests
{
[TestMethod]
public void Index_Always_CallsRepository()
{
// Arrange
var repository = new Mock<IUsersRepository>();
var controller = CreateValidUsersController(repository.Instance);
// Act
var result = controller.Index();
// Assert
Assert.IsTrue(repository.IsCalled);
}
// Factory method to simplify creation of the class under test with its dependencies
private UsersController CreateValidUsersController(params object[] deps) {
return new UsersController(
deps.OfType<IUsersRepository>().SingleOrDefault() ?? Fake<IUsersRepository>()
// other dependencies here
);
}
private static T Fake<T>() => (new Mock<T>()).Instance;
}
This does however, force you to change MVC's default IControllerFactory, since out-of-the-box, MVC can only handle controllers with a default constructor. But this is trivial and looks as follows:
public sealed class CompositionRoot : DefaultControllerFactory
{
private static string connectionString =
ConfigurationManager.ConnectionStrings["app"].ConnectionString;
protected override IController GetControllerInstance(RequestContext _, Type type) {
if (type == typeof(UsersController))
return new UsersController(new UsersRepository());
// [other controllers here]
return base.GetControllerInstance(_, type);
}
}
Your new controller factory can be hooked into MVC as follows:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start() {
ControllerBuilder.Current.SetControllerFactory(new CompositionRoot());
// the usual stuff here
}
}
You can find a more complete example here.
You could add a constructor that allows you to supply a mock of IUsersRepository. Your default constructor would call this with the instance from the DependencyResolver, like this:
public class UsersController : Controller
{
private IUsersRepository UsersRepository { get; }
public UsersController(IUsersRepository usersRepository)
{
UsersRepository = usersRepository;
}
public UsersController():this(DependencyResolver.Current.GetService(typeof(IUsersRepository)) as IUsersRepository)
{
}
public ActionResult Index ()
{
MyUserDefinedModel data = UsersRepository.MyRepository();
return View(data);
}
}

How to implement DIP in an MVC environment?

Dependency Inversion Principle states that higher level modules should not depend on lower level modules. Both should depend on abstractions. I do understand that principle. But as I'm using ASP.NET MVC, I often get my controller methods as:
public ActionResult MyAction(string userValue)
{
User user = MyDatabase.GetUser();
if (!user.CheckSomeCondition(userValue))
{ //Something failed. Try again.
return View();
}
user.Update(userValue);
return RedirectToAction("Success");
}
I believe this violates DIP because the result of my controller depends on the User class. In this scenario, I cannot think of a way of decoupling the Controller from my User class. Is there a way to get rid of this dependency? Or is this just fine to do it this way in this scenario?
create an interface and inject an implementation on that to your mvc controller.
public interface IMyDataAccess
{
User GetUser();
}
Create your implementation now.
public class MyMongoDBDataAccess : IMyDataAccess
{
public User GetUser()
{
//return a user from my fancy db
}
}
Now in your controller
public class HomeController : Controller
{
IMyDataAccess dao;
public HomeController(IMyDataAccess myDataAccess)
{
this.dao=myDataAccess;
}
public ActionResult MyAction(string userValue)
{
User user=this.dao.GetUser();
//return something to the view as needed.
}
}
You may use any dependency injection framework like Unity to inject the desired implementation of your interface to your controller.
If you want bring in more layers like Business Layer, Service layer, follow the same thing.
If your application structure is like below, than you are implementing DIP (Dependency Inversion Principle).
DIP says that a layer in your application should depend on a interface, not by an implementation. Like below, Service is depending on IDatabase not MyDatabase
public interface IDatabase {
Update(User user);
}
public interface MyDatabase : IDatabase
{
public Update(User user) {
// update user
}
}
public interface IService {
Update(string user);
}
public class Service : IService
{
private IDatabase _database;
public Service(IDatabase database)
{
_database = database;
}
public Update(User user) {
_database.Update(user);
}
}
DIP also says that high level modules like MvcController don't need to know/depend on low level modules, MyDatabase.
public class MvcController : Controller
{
private IService _service;
private IUserValidator _userValidator;
public MvcController(IService service, IUserValidator userValidator) // depending on abstraction
{
_service = service;
_userValidator = userValidator;
}
public ActionResult MyAction(string userValue)
{
if (!_userValidator.CheckSomeCondition(userValue))
{ //Something failed. Try again.
return View();
}
User user = _service.GetUser();
user.UserValue = userValue;
_service.Update(user);
return RedirectToAction("Success");
}
}
Note:
I recommend to take a look on 3-tier-architecture doing this you will improve your understanding of SOLID and your application will be more organized.
If in your case MyDatabase is a module Higher then User than, you are not respecting DIP principles, because your Controller make uses of a lower module.
DIP or IoC says High-level modules should not depend on low-level modules. Both should depend on abstractions.
It means that High Level class should not depend on concrete class but it should depend on Interface.
But here you are talking about User class which seems a DTO (Data Transfer Object).
So In that case, You must have to use that DTO in your last layer to either return data or deal with data.
But you should not be using POCO classes provided by entity framework, Instead you should create and use your DTO classes as ViewModels.
Ups, this is how I do it when using ASP.NET Web API, not sure if this works MVC Controller. But there is Unity.MVC (v3 or v4 or v5) lib (Unity.Mvc4) that you can use!
And you can wire it up like this, you should call this code in the Application_Start event!
public static class WebApiBootstrapper
{
public static void Init(IUnityContainer container)
{
GlobalConfiguration.Configure(config =>
{
config.DependencyResolver = new WebApiDependencyResolver(container); // DI container for use in WebApi
config.MapHttpAttributeRoutes();
WebApiRouteConfig.RegisterRoutes(RouteTable.Routes);
});
// Web API mappings
// All components that implement IDisposable should be
// registered with the HierarchicalLifetimeManager to ensure that they are properly disposed at the end of the request.
container.RegisterType<IYourController, YourController>(
new HierarchicalLifetimeManager(), new InjectionConstructor(typeof(IMyDataBase)));
}
}
But before running tthis code you have to register the type mapping
container.RegisterType<IMyDatabse, MyDataBase>();
And you also have to implement the DependencyResolver class:
public class WebApiDependencyResolver : IDependencyResolver
{
protected IUnityContainer container;
public WebApiDependencyResolver(IUnityContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
this.container = container;
}
public object GetService(Type serviceType)
{
try
{
return container.Resolve(serviceType);
}
catch (ResolutionFailedException)
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return container.ResolveAll(serviceType);
}
catch (ResolutionFailedException)
{
return new List<object>();
}
}
public IDependencyScope BeginScope()
{
var child = container.CreateChildContainer();
return new WebApiDependencyResolver(child);
}
public void Dispose()
{
container.Dispose();
}
}
In your controller:
public class YourController : ApiController, IYourController
{
IDataBase _db;
public PlayGroundController(IDataBase db)
{
_db = db;
}

At what point in a Web API app can I intercept the URI arguments and route the calls accordingly?

Note: This question is indeed somewhat similar to this one, but I think I can put it in a simpler and more specific way.
I'm using Castle Windsor to intercept the URI passed to my Web API app to register the appropriate concrete class to the Controller's constructor.
What I want to be able to do is pass a "site number" on the URI, perhaps always as either the first or last arg. IOW, for site 42, instead of
http://localhost:28642/api/platypi/GetAll
...it would be:
http://localhost:28642/api/platypi/42/GetAll
-or:
http://localhost:28642/api/platypi/GetAll/42
When my Web API app first "sees"/intercepts the URI, I want to note that site number so that I can assign the desired concrete Repository to be registered by Castle Windsor. I want to be able to do this:
public class RepositoriesInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
if (siteNum == 42)
{
container.Register(
Component.For<IDepartmentRepository>().ImplementedBy<DepartmentRepository42>().LifestylePerWebRequest(),
Component.For<IInventoryItemRepository>().ImplementedBy<InventoryItemRepository42>().LifestylePerWebRequest(),
. . .
}
else if (siteNum = 77)
{
container.Register(
Component.For<IDepartmentRepository>().ImplementedBy<DepartmentRepository77>().LifestylePerWebRequest(),
Component.For<IInventoryItemRepository>().ImplementedBy<InventoryItemRepository77>().LifestylePerWebRequest(),
. . .
}
In this way I can give site 42 its data, site 77 its data (each site uses a different database that share a common schema).
So: At which point in my Web API app's lifecycle can I hijack the URI, so as to assign the appropriate val to the global siteNum variable, so that it has been assigned before the IWindsorInstaller method runs?
UPDATE
Thanks to Mr. Slate, but if I were to do that, would this Controller code:
public DepartmentsController(IDepartmentRepository deptsRepository)
{
if (deptsRepository == null)
{
throw new ArgumentNullException("deptsRepository");
}
_deptsRepository = deptsRepository;
}
...become:
public DepartmentsController(IDepartmentRepository deptsRepository)
{
if (deptsRepository == null)
{
throw new ArgumentNullException("deptsRepository");
}
_deptsRepository = deptsRepository(siteNum);
}
...or???
And I'm still left with the problem of where do I intercept the incoming URI before Castle Windsor / the Controller gets it, so that I can set the appropriate value to the global / siteNum var?
There are a number of extensions points that you can use to achieve this, personally I use this one for a similar result.
Create a custom model binder by extending IModelBinder something like this:
public class SiteManagerModelBinder : IModelBinder
{
#region IModelBinder Members
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (bindingContext.Model != null)
{
throw new InvalidOperationException("Cannot update instances");
}
// Apply your condition to determine if site number is in Url.
if (controllerContext.RouteData.Values['siteNum']!=null)
{
// probably want to resolve this from container just hard coding as example, assumption is that SiteManager, does the repository bits for you.
return new SiteManager((int)controllerContext.RouteData.Values['siteNum']);
}
return null;
}
#endregion
}
Okay so now we jus tneed to register our new ModelBinder:
protected void Application_Start()
{
ModelBinders.Binders.Add(typeof(SiteManager), new SiteManagerModelBinder ());
Okay and now in our controller all we do is add the SiteManager as a parameter of any Action and it will get filled in by our ModelBinder.
public class DepartmentsController: Controller {
public ActionResult AnyAction(SiteManager siteManager, int whateverElse, ViewModel model)
{
}
}
I'd change the constructor on DepartmentRepository() to pass the site num and use that to get the connectionstring. Then in your webconfig create a connectionstring for each site.
Here is a different way you could try rather using Castle Windsor which might be better:
Create a repository factory like this:
public interface IRepositoryFactory
{
Repository CreateRepositoryById(int id);
}
Create a component selector to select the correct repository based on the name like this:
public class RepositoryFactorySelector : DefaultTypedFactoryComponentSelector
{
protected override string GetComponentName(MethodInfo method, object[] arguments)
{
return (method.Name.EndsWith("ById") && arguments.Length >= 1 && arguments[0] is int)
? string.Format("Repository{0}", arguments[0])
: base.GetComponentName(method, arguments);
}
}
Register your repositories like this:
public class RepositoryInstaller : IWindsorInstaller
{
#region IWindsorInstaller Members
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly().BasedOn<Repository>().Configure(c=>c.Named(c.Implementation.Name)));
container.Register(
Component.For<RepositoryFactorySelector, ITypedFactoryComponentSelector>().LifestyleSingleton(),
Component.For<IRepositoryFactory>().AsFactory(c => c.SelectedWith<RepositoryFactorySelector>()));
}
#endregion
}
As long as Windsor is loading your controllers using a constroller factory you can now do this:
public class HomeController : Controller
{
public IRepositoryFactory RepositoryFactory { get; set; }
public ActionResult Index(int siteNum)
{
var repository = RepositoryFactory.CreateRepositoryById(siteNum);
// tada!
return View();
}
}
For reference my global.asax.cs:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
Container = new WindsorContainer();
Container.AddFacility<TypedFactoryFacility>();
Container.Install(FromAssembly.This());
var controllerFactory = new WindsorControllerFactory(Container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
}
public WindsorContainer Container;
}
And my repositories:
public abstract class Repository
{
}
public class Repository1 : Repository
{
}
public class Repository2 : Repository
{
}

Categories