I am new to Autofac and IoC so now I try to build my firs ASP.NET MVC 3 application with IoC.
Here with this code I try manually to register ProductController (in Global.asax).
protected void AutofacIocRegistration()
{
var builder = new ContainerBuilder();
//PRODUCT CONTROLLER
var registration = builder.Register(c => new ProductsController(c.Resolve<IProductRepository>()));
builder.RegisterType<ProductsController>().InstancePerHttpRequest();
SqlProductsRepository productRepository = new SqlProductsRepository(DATABASE_CONNECTION);
builder.RegisterInstance(new ProductsController(productRepository));
IContainer container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
When I build the application for the first time, I get list of 4 products in the HTML page (as I expected), but when I refresh the page or go "next" to other list of products then I get the error
A single instance of controller
'WebUI.Controllers.ProductsController'
cannot be used to handle multiple
requests. If a custom controller
factory is in use, make sure that it
creates a new instance of the
controller for each request.
As you can see in this code
builder.RegisterType<ProductsController>().InstancePerHttpRequest();
I try to solve this with InstancePerHttpRequest() method but unsuccessful, if you have any suggestion please post.
Thanks for any help in advance.
use the below method to register your controllers
builder.RegisterControllers(Assembly.GetExecutingAssembly());
if you are using constructor injection in the controllers you can register your repository like this
builder.RegisterType<SqlProductsRepository>().As<IProductRepository>();
this way it will create the object graph when it needs to create the controller
Related
In an Azure Mobile App using the .NET backend, I need one controller to look up an entity handled by a second controller. For example, in the Todo Quickstart project from the Azure team, imagine adding a UserController which handles user management. In TodoItemController, I need to call UserController.GetUser(id) to check if a user is authorized to post a new TodoItem.
In TodoItemController.cs:
var userController = new UserController();
var user = userController.GetUser("12345");
if (user.IsAuthorized)
{
// Insert TodoItem
}
The above code throws an exception when TableController.Lookup() is called in UserController.GetUser(). The exception says that the request parameter cannot be null. My guess is that something is missing because I created the UserController myself, instead of it being created by the framework.
How can I make this work?
This appears to work in TodoItemController.cs:
var context = new todoProjectContext();
var userDomainManager = new EntityDomainManager<User>(context, Request);
var user = userDomainManager.Lookup(id).Queryable.FirstOrDefault();
if (user.isAuthorized)
{
// Insert item
}
Not sure it's the best solution though.
I am using web api with unity IOC.
web api client passes client-id in request header and based on this value dependencies are resolved to create a external dll's method instance.
creation of this instance take around 6-7 seconds which is creating performance issues in web api.
What I want is to prevent instance creation for call with same client-id in header.
This is how I have implemented till now:-
//========================== ArchiveFactory ==========================
ArchiveFactory archiverFactory = (HttpRequest httpRequest) =>
{
container.RegisterType<IArchive, Archive>("Archive",
new HierarchicalLifetimeManager(),
new InjectionConstructor(
new ResolvedParameter<IStoreClient>(),
Helper.GetArchiveContext(httpRequest))
);
return container.Resolve<IArchive>("Archive");
};
container.RegisterInstance(archiverFactory);
To be specific in my requirement - I am calling amazon services to retrieve images and there is a corporate dll which invokes amazon.
You can use caching mechanism at the controller/API layer(e.g Strathweb.CacheOutput.WebApi2) and you can decorate the controller method like this below. It's can cache based on parameter so if request comes in with same parameter, it will return results from cache.
[HttpGet]
[Route("")]
[CacheOutput(ServerTimeSpan = 60, ExcludeQueryStringFromCacheKey = true)]
public IHttpActionResult GetProducts(string clientId)
{
var product = new List<Product>();
return Ok(product);
}
Also, you might want to check the class constructor that you are trying to instantiate for issues that is taking it too slow. You may want to consider using lazy loading too if that will apply.
I have the following in an Area Registration of my ASP.NET MVC 3 project running on .NET Framework v4.0:
context.MapRoute(null,
"YardJob/{location}/{from}",
new { controller = "YardJob",
action = "List",
from = DateTime.Now });
My question is:
If the routing engine uses the default route value for 'from', will the List method on the controller always be invoked with the current date and time?
Is there any caching in the routing engine that may cause the default route value to be reused among requests?
Thanks,
As the accepted answer explains, this isn't possible. However, for the sake of completeness, here is how you would work around this:
Route:
context.MapRoute(null,
"YardJob/{location}/{from}",
new { controller = "YardJob",
action = "List",
from = UrlParameter.Optional }
);
Controller action:
public ActionResult List (string location, DateTime from)
{
if (from == null)
from = DateTime.Now;
}
the process of register the routes is executed when the application starts, so if you put DateTime.Now the default parameter for from field would be the time when the application starts, change only when the AppPool recycles
when application starts?
when the first resource (such as a page) in an ASP.NET application is requested. The Application_Start method in Global.asax is called only one time during the life cycle of an application. You can use this method to perform startup tasks such as loading data into the cache and initializing static values.
check out the life cicle
I'm creating a CMS using ASP.NET MVC, and by design, I've decided that each plugin (add-on) should have a key in the incoming HTTP request. Thus, I have this general route in my host application:
{pluginKey}/{controller}/{action}/{id}
I've created a custom controller factory which implements IControllerFactory and of course, it has a method to create controllers base on the ReqeustContext and controller name. However, I want to create an artificial HttpContext (alongside all other relevant objects like HttpRequest, RequestContext, RouteData, etc.) so that controllers of plugins won't misinterpret these URL segments wrongly. In other words, I want to cut the first part of the incoming URL, and make plugins think that they're processing this URL:
{controller}/{action}/{id}
How can I achieve this?
While you could create a new implementation of all the context classes, it seems like a bit of overkill. Why not use a derived Route Handler that applies the filtering functionality before returning the HttpHandler? Here's an example:
// To avoid conflicts with similarly named controllers, I find it to be good practice
// to create a route constraint with the set of all plugin names. If you don't have
// this function already, you should be able to access it with reflection (one time
// per app lifecycle) or you hard-code them. The point is to have a regex which ensures
// only valid plugins will get selected
string[] pluginNames = GetPluginNames();
string pluginNameRegex = string.Join("|",pluginNames);
Route pluginRoute = new Route (
url: "{pluginKey}/{controller}/{action}/{id}",
defaults: null,
constraints: new RouteValueDictionary(new { pluginKey = pluginNameRegex }),
routeHandler: new PluginRouteHandler()
});
// The custom route handler can modify your route data after receiving the RequestContext
// and then send it to the appropriate location. Here's an example (markdown code/untested)
// Note: You don't have to inherit from MvcRouteHandler (you could just implement IRouteHandler
// but I'm assuming you want Mvc functionality as the fallback)
public class PluginRouteHandler : MvcRouteHandler
{
public PluginRouteHandler(IControllerFactory controllerFactory)
: base(controllerFactory)
{}
protected override IHttpHandler GetHttpHandler(RequestContext requestContext){
if(ValidatePluginRoute(requestContext))
{
// we are going to remove the pluginKey from the RequestContext, It's probably wise
// to go ahead and add it to HttpContext.Items, in case you need the data later
requestContext.HttpContext.Items["pluginKey"] = requestContext.RouteData.Values["pluginKey"];
// now let's get ride of it, so your controller factory will process the
// requestContext as you have described.
requestContext.Values.Remove("pluginKey");
// the route will now be interpreted as described so let the flow go to the MvcRouteHandler's method
}
return base.GetHttpHandler(requestContext);
}
static bool ValidatePluginRoute(RequestContext requestContext){
return requestContext.RouteData.ContainsKey("pluginKey");
}
}
I know how to get the current controller name
HttpContext.Current.Request.RequestContext.RouteData.Values["controller"].ToString();
But is there any way to get the current controller instance in some class (not in an action and not in a view)?
By default you can only access the current Controller inside a controller with ControllerContext.Controller or inside a view with ViewContext.Context. To access it from some class you need to implement a custom ControllerFactory which stores the controller instance somewhere and retrieve it from there. E.g in the Request.Items:
public class MyControllerFactory : DefaultControllerFactory
{
public override IController CreateController(RequestContext requestContext, string controllerName)
{
var controller = base.CreateController(requestContext, controllerName);
HttpContext.Current.Items["controllerInstance"] = controller;
return controller;
}
}
Then you register it in your Application_Start:
ControllerBuilder.Current.SetControllerFactory(new MyControllerFactory());
And you can get the controller instance later:
public class SomeClass
{
public SomeClass()
{
var controller = (IController)HttpContext.Current.Items["controllerInstance"];
}
}
But I would find some another way to pass the controller instance to my class instead of this "hacky" workaround.
Someone will have to correct me if what I am doing is detrimental to the whole Asp.Net page life cycle / whatever but surely you can do this:
In controller
ViewBag.CurrentController = this;
In view
var c = ViewBag.CurrentController;
var m1 = BaseController.RenderViewToString(c, "~/Views/Test/_Partial.cshtml", null);
In my case, I had a base controller that all controllers extend. In that base controller lived a static method called RenderViewToString and it required a controller. Since I figured I could just instantiate a new instance of an empty controller at this point for c, I just sent it to the view in the lovely ViewBag container that exists in the world of Asp.Net MVC. For reasons I could not go into now, I could not retrieve the string in the controller and send just that back to the view (this was what I had done earlier before requirements changed).
The reason I have done it this way is in other languages like PHP and JS, there are similar simple ways to transfer classes around.