MVC 4 View engine search locations - asp.net-mvc

I have implemented mcfea answer on the bottom of this post but it is not working.
Adding sub-directory to "View/Shared" folder in ASP.Net MVC and calling the view
I have a subfolder under Views/Shared called Timesheet.
This the exception.
The view 'Timesheet' or its master was not found or no view engine
supports the searched locations. The following locations were
searched: ~/Views/Home/Timesheet.aspx ~/Views/Home/Timesheet.ascx
~/Views/Shared/Timesheet.aspx ~/Views/Shared/Timesheet.ascx
~/Views/Home/Timesheet.cshtml ~/Views/Home/Timesheet.vbhtml
~/Views/Shared/Timesheet.cshtml ~/Views/Shared/Timesheet.vbhtml
Malcolm
public class MylesterViewEngine : RazorViewEngine
{
private static readonly string[] NewPartialViewFormats =
{
"~/Views/{1}/Timesheet/{0}.cshtml",
"~/Views/Shared/Timesheet/{0}.cshtml"
};
private static List<string> AreaRegistrations;
public MylesterViewEngine()
{
AreaRegistrations = new List<string>();
BuildAreaRegistrations();
base.PartialViewLocationFormats = base.PartialViewLocationFormats.Union(NewPartialViewFormats).ToArray();
base.PartialViewLocationFormats = base.PartialViewLocationFormats.Union(AreaRegistrations).ToArray();
}
private static void BuildAreaRegistrations()
{
string[] areaNames = RouteTable.Routes.OfType<Route>()
.Where(d => d.DataTokens != null && d.DataTokens.ContainsKey("area"))
.Select(r => r.DataTokens["area"].ToString()).ToArray();
foreach (string areaName in areaNames)
{
AreaRegistrations.Add("~/Areas/" + areaName + "/Views/Shared/Timesheet/{0}.cshtml");
AreaRegistrations.Add("~/Areas/" + areaName + "/Views/{1}/Timesheet/{0}.cshtml");
}
}
}
protected void Application_Start()
{
//AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new MylesterViewEngine());
//var unityContainer = ModelContainer.Instance;
//DependencyResolver.SetResolver(new UnityDependencyResolver(unityContainer));
Bootstrapper.Initialise();
}
EDIT 2: Even this doesnt work
protected void Application_Start()
{
//AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
RazorViewEngine razorEngine = ViewEngines.Engines.OfType<RazorViewEngine>().FirstOrDefault();
if (razorEngine != null)
{
string[] newPartialViewFormats = new[] {
"~/Views/{1}/Timesheet/{0}.cshtml",
"~/Views/Shared/Timesheet/{0}.cshtml"
};
razorEngine.PartialViewLocationFormats =
razorEngine.PartialViewLocationFormats.Union(newPartialViewFormats).ToArray();
}
//ViewEngines.Engines.Clear();
//ViewEngines.Engines.Add(new MylesterViewEngine());
//var unityContainer = ModelContainer.Instance;
//DependencyResolver.SetResolver(new UnityDependencyResolver(unityContainer));
Bootstrapper.Initialise();
}

The answer you linked to was for specifically returning partial views, not main views. So the code inside it says something like this:
string[] NewPartialViewFormats =
{
"~/Views/{1}/Timesheet/{0}.cshtml",
"~/Views/Shared/Timesheet/{0}.cshtml"
};
base.PartialViewLocationFormats =
base.PartialViewLocationFormats.Union(NewPartialViewFormats).ToArray();
Notice that it uses the PartialViewLocationFormats property. If you want to use this method for all views, you should also add your formats to the ViewLocationFormats property:
string[] NewPartialViewFormats =
{
"~/Views/{1}/Timesheet/{0}.cshtml",
"~/Views/Shared/Timesheet/{0}.cshtml"
};
//Add to partial views
base.PartialViewLocationFormats =
base.PartialViewLocationFormats.Union(NewPartialViewFormats).ToArray();
//Add to main views
base.ViewLocationFormats =
base.ViewLocationFormats.Union(NewPartialViewFormats).ToArray();

If you passing under subfolder than need to pass full cshtml page path. If you don't want provide full path than simple add page under Shared folder.

Related

HelpPage Dont find View after routing areas

I launched the API Help Page for the my web API project.
Now the question is that after Routing on other Areas my Help page can not be found index.cshtml. This is global.asax code and I posted a picture of the error message
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AreaRegistration.RegisterAllAreas();
MvcHandler.DisableMvcResponseHeader = true; /*Version Discloser 5 in 10 point*/
//Infrastructure.ConnectionStringEncryption.EncryptConnString();
//GlobalFilters.Filters.Add(new HandleAntiforgeryTokenErrorAttribute() { ExceptionType = typeof(HttpAntiForgeryException) });
}
My helppage controller code:
public class HelpController : Controller
{
private const string ErrorViewName = "Error";
public HelpController()
: this(GlobalConfiguration.Configuration)
{
}
public HelpController(HttpConfiguration config)
{
Configuration = config;
}
public HttpConfiguration Configuration { get; private set; }
public ActionResult Index()
{
ViewBag.DocumentationProvider = Configuration.Services.GetDocumentationProvider();
return View(Configuration.Services.GetApiExplorer().ApiDescriptions);
}
public ActionResult Api(string apiId)
{
if (!String.IsNullOrEmpty(apiId))
{
HelpPageApiModel apiModel = Configuration.GetHelpPageApiModel(apiId);
if (apiModel != null)
{
return View(apiModel);
}
}
return View(ErrorViewName);
}
public ActionResult ResourceModel(string modelName)
{
if (!String.IsNullOrEmpty(modelName))
{
ModelDescriptionGenerator modelDescriptionGenerator = Configuration.GetModelDescriptionGenerator();
ModelDescription modelDescription;
if (modelDescriptionGenerator.GeneratedModels.TryGetValue(modelName, out modelDescription))
{
return View(modelDescription);
}
}
return View(ErrorViewName);
}
}
And my help page view is here:

Access asp.net mvc controller ActionResult

i want to access action result in controller(my controlelr is HotelController action is Index)
(http://localhost:9001/Hotel/Index) it gives below error
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /Hotel/Index
Hotel controller
public class HotelController : Base.BoxyController
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
ViewBag.Title = "SonDakka - Otel";
}
public ActionResult Index(string culture)
{
.........
BoxyController
public class BoxyController : MainController
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
..........
MainController
public class MainController : SiteController
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
......
SiteController
[ExitHttpsIfNotRequired]
public class SiteController : Controller
{
public Account Me { get; set; }
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
.......
and this is my global.asax
using System;
using System.Data.Entity;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
using Tourism.Data;
using Tourism.Data.Mvc.Authorization;
using Tourism.Data.Mvc.Routing;
namespace Tourism
{
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(TourismContext db, RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
var cultures = db.Cultures.Select(c => c.Code).ToArray();
routes.MapRoute
(
"Ajax",
"{culture}/{controller}/{action}/{id}",
new { id = UrlParameter.Optional },
new { culture = new ArrayRouteConstraint(true, cultures), controller = new ArrayRouteConstraint(true, "Ajax") }
).RouteHandler = new GlobalizedRouteHandler();
routes.Add
(
"Page",
new GlobalizedPageRoute
(
"{culture}/{path}",
null,
new RouteValueDictionary { { "culture", new ArrayRouteConstraint(true, cultures) } },
new GlobalizedRouteHandler()
)
);
routes.Add
(
"Route",
new GlobalizedRoute
(
"{culture}/{path}/{slug}/{id}",
new RouteValueDictionary { { "culture", UrlParameter.Optional }, { "path", UrlParameter.Optional }, { "slug", UrlParameter.Optional }, { "id", UrlParameter.Optional } },
new RouteValueDictionary { { "culture", new ArrayRouteConstraint(false, cultures) } },
new GlobalizedRouteHandler()
)
);
}
protected void Application_Start()
{
Database.SetInitializer<TourismContext>(null);
using (var db = new TourismContext())
{
#if !DEBUG
if (!db.Database.CompatibleWithModel(true))
{
System.Web.HttpRuntime.UnloadAppDomain();
throw new Exception("Veritabanı değişikliği tespit edildi.");
}
#endif
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(db, RouteTable.Routes);
}
}
protected void Application_PostAuthenticateRequest()
{
if (Request.IsAuthenticated)
{
Context.User = System.Threading.Thread.CurrentPrincipal =
new AuthorizationPrincipal(Context.User.Identity);
}
}
}
}
Because this is due to Razor engine unable to find Thanks action in Hotel controller. You need to make a Thanks action with in Hotel controller like this:
public class HotelController : Base.BoxyController
{
public ActionResult Thanks(string culture)
{
return View();
}
}
And also make sure to create a view in Hotel folder with your html code.
Based on the route config you posted, your URL should be with culture, for example:
http://localhost:9001/en/Hotel/Index
Notice the en before Hotel. It could be any value that is valid in your database.

mvc 4 mef import/export confusion

I'm having a difficult time wrapping my head around Mef and how imports and exports work. My project structure is as follows.
Projects:
MefMVPApp (Main MVC 4 app)
MefMVCFramework.Common(Interfaces shared between the projects)
MefMVCDemo.Plugins.OrderStatus (pluggable area.)
MefMVCDemo.Plugins.Data (Repository for OrderStatus)
OrderStatus.Models(domain models shared between the projects)
The goal of the main Mvc App will be to host plug-able areas via mef.
The OrderStatus Area has a controller called OrderStatusController and is decorated with the Export Attribute and a ImportingConstructor.
[Export(typeof(IController))]
[ExportMetadata("controllerName", "OrderStatus")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class OrderStatusController : Controller
{
private readonly IRepository<OrderStatusApp.OrderStatusResponse>_repository ;
[ImportingConstructor]
public OrderStatusController(IRepository<OrderStatusApp.OrderStatusResponse> oRepository)
{
_repository = oRepository;
}
public ActionResult Index()
{
var model = _repository.GetAll();
return View();
}
}
IRepository is a class in the MefMVCFramework.Common assembly and will be used for generic CRUD operations.
public interface IRepository<T> where T : class
{
IEnumerable<T> GetAll();
T GetById(int id);
void Add(T entity);
int SaveOrUpdate(T entity);
bool Delete(T entity);
bool Delete(int id);
}
The MefMVCDemo.Plugins.Data assembly contains a Class called OrderManagementRepository that inherents for the generic repository and is marked with an Export Attribute.
[Export(typeof(IRepository<OrderStatusApp.OrderStatusResponse>))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class OrderManagementRepository : IRepository<OrderStatusApp.OrderStatusResponse>
{
private readonly JsonServiceClient _client;
public OrderManagementRepository()
{
_client = new JsonServiceClient("http://localhost:52266");
}
public IEnumerable<OrderStatusApp.OrderStatusResponse> GetAll()
{
throw new NotImplementedException("Can not get all");
}
public OrderStatusApp.OrderStatusResponse GetById(int id)
{
throw new NotImplementedException();
}
public void Add(OrderStatusApp.OrderStatusResponse entity)
{
throw new NotImplementedException();
}
public int SaveOrUpdate(OrderStatusApp.OrderStatusResponse entity)
{
throw new NotImplementedException();
}
public bool Delete(OrderStatusApp.OrderStatusResponse entity)
{
throw new NotImplementedException();
}
public bool Delete(int id)
{
throw new NotImplementedException();
}
}
Using Mefx tool I am able to see my parts and there are no rejection.
mefx /dir:C:\
Source.PreBranch.Keep\Prototypes\Projects\MefDemoApp\mefMVC4App\bin /parts
MefMVCDemo.Plugins.Data.OrderManagementRepository
mefMVCDemo.Plugins.OrderStatus.Controllers.OrderStatusController
MefMVCDemo.Plugins.OrderStatus.Verbs.OrderStatusVerb
I can see my import.
mefx /dir:C:\
Source.PreBranch.Keep\Prototypes\Projects\MefDemoApp\mefMVC4App\bin /imports
MefMVCFramework.Common.IRepository(OrderStatus.Models.OrderStatusApp+OrderStatus
Response)
MefMVCFramework.Common.IRepository(OrderStatus.Models.OrderStatusApp+OrderStatus
Response)
Now when browse my main mvc site with the /orderstatus uri I get the following error:
No parameterless constructor defined for this object.
Adding a default constructor to the OrderStatusController that takes no overloads doesn't seem to work.
I guess the question is what am I doing wrong? Why does my interface in the constructor all way end up being null and why is there an mvc error about the "No parameterless constructor defined for this object".
The default controller factory in MVC tries to create the controller using a parameterless constructor. If you want to change this behavior, then you need to create your own custom controller factory.
Here is an example of a ControllerFactory using imports/exports on controllers
I'm using MEF for importing some parts to my app but my controllers are not imported/exported, so I created the following controller factory
public class ControllerFactory : IControllerFactory
{
private readonly CompositionContainer _container;
private IControllerFactory _innerFactory;
/// <summary>
/// Constructor used to create the factory
/// </summary>
/// <param name="container">MEF Container that will be used for importing</param>
public ControllerFactory(CompositionContainer container)
{
_container = container;
_innerFactory = new DefaultControllerFactory();
}
/// <summary>
/// Method used for create the controller based on the provided name. It calls the
/// constructor of the controller passing the MEF container
/// </summary>
/// <param name="requestContext">Context of the request</param>
/// <param name="controllerName">Name of the controller provided in the route</param>
/// <returns>The controller instance</returns>
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
Type controllerType = FindControllerByName(controllerName);
var args = new object[] { this._container };
var controller = (IController)Activator.CreateInstance(controllerType, args);
return controller;
}
/// <summary>
/// This methods looks into the current Assembly for the Controller type
/// </summary>
/// <param name="name">The controller name provided in the route</param>
/// <returns>The controller type</returns>
private static Type FindControllerByName(string name){
var a = Assembly.GetAssembly(typeof(ControllerFactory));
var types = a.GetTypes();
Type type = types.Where(t => t.Name == String.Format("{0}Controller", name)).FirstOrDefault();
return type;
}
public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior(System.Web.Routing.RequestContext requestContext, string controllerName)
{
return System.Web.SessionState.SessionStateBehavior.Default;
}
public void ReleaseController(IController controller)
{
var disposableController = controller as IDisposable;
if (disposableController != null)
{
disposableController.Dispose();
}
}
}
Thank you pollirrata for pointing me in the right direction.
I had to change a few things to get this to work.
1.) I added an interface called INameMetadata to my MefMVCFramework.Common project.
public interface INameMetadata
{
string Name { get; }
}
2.) Modified My ExportMetadata Tag on my controller export to be Name, OrderStatus.
[Export(typeof(IController))]
[ExportMetadata("Name", "OrderStatus")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class OrderStatusController : Controller
{
private IRepository<OrderStatusApp.OrderStatusResponse> _repository;
[ImportingConstructor]
public OrderStatusController(IRepository<OrderStatusApp.OrderStatusResponse> oRepository)
{
_repository = oRepository;
}
public ActionResult Index()
{
var model = _repository.GetById(47985);
return View(model);
}
}
3.) Created the MefControllerFactory (based off what pollirrata posted but modified to look for the Metadata)
public class MefControllerFactory : IControllerFactory
{
private string _pluginPath;
private readonly DirectoryCatalog _catalog;
private readonly CompositionContainer _container;
private DefaultControllerFactory _defaultControllerFactory;
public MefControllerFactory(string pluginPath)
{
_pluginPath = pluginPath;
_catalog = new DirectoryCatalog(pluginPath);
_container = new CompositionContainer(_catalog);
_defaultControllerFactory = new DefaultControllerFactory();
}
public MefControllerFactory(CompositionContainer compositionContainer)
{
_container = compositionContainer;
_defaultControllerFactory = new DefaultControllerFactory();
}
#region IControllerFactory Members
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
//IController controller = null;
var controller = _container.GetExports<IController,INameMetadata>()
.Where(e=>e.Metadata.Name.Equals(controllerName))
.Select(e=>e.Value).FirstOrDefault();
if (controller == null)
{
throw new HttpException(404, "Not found");
}
return controller;
}
public void ReleaseController(IController controller)
{
var disposable = controller as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
#endregion
public SessionStateBehavior GetControllerSessionBehavior(System.Web.Routing.RequestContext requestContext, string controllerName)
{
return SessionStateBehavior.Default;
}
}
4.) I created a Class called MefConfig in Main MVC app and moved it to the App_Start Dir.
public static class MefConfig
{
public static void RegisterMef()
{
//var builder = new RegistrationBuilder();
//builder.ForTypesDerivedFrom<IRepository<OrderStatusApp.OrderStatusResponse>>().Export<IRepository<IRepository<OrderStatusApp.OrderStatusResponse>>>();
var directoryCatalog = new DirectoryCatalog(HostingEnvironment.MapPath("~/bin"), "*.dll");
var container = new CompositionContainer(directoryCatalog, true);
ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(container));
//Working
//ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(HostingEnvironment.MapPath("~/bin")));
// Install MEF dependency resolver for MVC
var resolver = new MefDependencyResolver(container);
DependencyResolver.SetResolver(resolver);
// Install MEF dependency resolver for Web API
GlobalConfiguration.Configuration.DependencyResolver = resolver;
var d = container.GetExportedValues<IRepository<OrderStatusApp.OrderStatusResponse>>();
//Mefx.
try
{
//var ci = new CompositionInfo(aggregateCatalog, container);
var ci = new CompositionInfo(directoryCatalog, container);
var partDef = ci.GetPartDefinitionInfo(typeof(IRepository<OrderStatusApp.OrderStatusResponse>));
//var possibleCauses = partDef.FindPossibleRootCauses();
var stringWriter = new StringWriter();
CompositionInfoTextFormatter.Write(ci, stringWriter);
var compStatString = stringWriter.ToString();
}
catch
{
}
MvcApplication.ActionVerbs = container.GetExports<IActionVerb, IActionVerbMetadata>();
}
}
5.) Load the Mefconfig from the global.asax.
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
//Register Mef
MefConfig.RegisterMef();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}

how to use AsyncController in MVC application using Ninject for DI?

Does anyone know how to use an AsyncController in a mvc application that uses Ninject for DI?
AsyncController works fine when i dont use ninject but i cant make them work together.
I added following in my sitemodule but no go.
Bind<IAsyncController>( ).To<AsyncController>( ).InSingletonScope( );
sorry for not explaining this in details.
my controller looks like this
[HandleError]
public class HomeController : AsyncController
{
public void IndexAsync( )
{
AsyncManager.OutstandingOperations.Increment( );
RssFeed feed = new RssFeed( );
feed.GetRssFeedAsyncCompleted += ( s, e ) =>
{
AsyncManager.Parameters[ "items" ] = e.Items;
AsyncManager.OutstandingOperations.Decrement( );
};
feed.GetRssFeedAsync( "http://feeds.abcnews.com/abcnews/topstories" );
}
public ActionResult IndexCompleted( IEnumerable<SyndicationItem> items )
{
ViewData[ "SyndicationItems" ] = items;
return View( );
}
}
and my global.asax looks like this
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
protected void Application_Start( )
{
AreaRegistration.RegisterAllAreas( );
RegisterRoutes( RouteTable.Routes );
}
}
this works fine. but as soon as i use ninject (ninject 2.0 ) i get 404 page not found error when i try to access the index page. this is how i am configuring ninject
public class MvcApplication : NinjectHttpApplication //System.Web.HttpApplication
{
#region IOC
static IKernel container;
public static IKernel Container
{
get
{
if ( container == null ) { container = new StandardKernel( new SiteModule( ) ); }
return container;
}
}
protected override IKernel CreateKernel( )
{
return Container;
}
#endregion
public static void RegisterRoutes( RouteCollection routes )
{
routes.IgnoreRoute( "{resource}.axd/{*pathInfo}" );
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
//protected void Application_Start()
//{
// AreaRegistration.RegisterAllAreas();
// RegisterRoutes(RouteTable.Routes);
//}
protected override void OnApplicationStarted( )
{
AreaRegistration.RegisterAllAreas( );
RegisterRoutes( RouteTable.Routes );
}
}
public class SiteModule : NinjectModule
{
public override void Load( )
{
}
}
Do i need to bind anything on my sitemodule?
BTW i am using Jeff Prosise's example which he posted in his blog Here
you can download his demo application and try Ninject-ify it :)
Any help appreciated.
It appears it's not working because the standard NinjectControllerFactory inserts a NinjectActionInvoker into the controller's ActionInvoker property. The NinjectActionInvoker is derived from ControllerActionInvoker. An AsyncController, however, uses ActionInvokers derived from AsyncControllerActionInvoker. for some reason, this causes the controller to not match the route, and it returns a 404.
The real fix would be a patch to Ninject to support construction of AsyncController with AsyncControllerActionInvokers.
However, in the meantime, here is a workaround:
in your Global.asax, add this override:
protected override Ninject.Web.Mvc.NinjectControllerFactory CreateControllerFactory()
{
return new MyNinjectControllerFactory( kernel );
}
and then add this class for MyNinjectControllerFactory:
public class MyNinjectControllerFactory : Ninject.Web.Mvc.NinjectControllerFactory
{
public MyNinjectControllerFactory( IKernel kernel ) : base( kernel ) { }
protected override IController GetControllerInstance( RequestContext requestContext, Type controllerType )
{
if ( controllerType == null )
{
// let the base handle 404 errors with proper culture information
return base.GetControllerInstance( requestContext, controllerType );
}
var controller = Kernel.TryGet( controllerType ) as IController;
if ( controller == null )
return base.GetControllerInstance( requestContext, controllerType );
//var standardController = controller as Controller;
//if ( standardController != null )
// standardController.ActionInvoker = CreateActionInvoker();
return controller;
}
}
this is a copy of the NinjectControllerFactory that leaves out the assignment of the ActionInvoker.
IF you have code that depends on dependencies being injected into your ActionFilters, you will need to create your own ActionInvoker that returns an AsyncControllerActionInvoker that uses Ninject. Look at the Ninject.Web.Mvc source for the NinjectActionInvoker.
Like dave pointed out a patch is needed for Ninject to support async controller and Remo says he'll work on it as soon as he has sometime. meantime you can use dave's workaround or try this. this is straight from horse's mouth. i posted a msg in ninject group and Remo responded with this .
AsyncControllers are currently not
supported. I'll add this as soon as I
have the time to implement it
properly. In the mean time you can use
apply the following changes to the
sources to add the support:
Make a copy of NinjectActionInvoker name it NinjectAsyncActionInvoker and
change base type to
AsyncControllerActionInvoker
Apply the following changes to NinjectControllerFactory diff --git
"a/C:\Users\REMOGL~1\AppData\Local\Temp\
\NinjectControllerFactory_HEAD.cs"
"b/C:\Projects\Ninject\
\ninject.web.mvc\mvc2\src\Ninject.Web.Mvc\
\NinjectControllerFactory.cs" index
2c225a1..3916e4c 100644
--- "a/C:\Users\REMOGL~1\AppData\Local\Temp\
\NinjectControllerFactory_HEAD.cs"
+++ "b/C:\Projects\Ninject\ninject.web.mvc\mvc2\src\
\Ninject.Web.Mvc\NinjectControllerFactory.cs"
## -53,10 +53,18 ## namespace
Ninject.Web.Mvc
if (controller == null)
return base.GetControllerInstance(requestContext,
controllerType);
var standardController = controller as
Controller;
var asyncController = controller as AsyncController;
if (asyncController != null)
{
asyncController.ActionInvoker =
CreateAsyncActionInvoker();
}
else
{
var standardController = controller as
Controller;
if (standardController != null)
standardController.ActionInvoker =
CreateActionInvoker();
}
if (standardController != null)
standardController.ActionInvoker =
CreateActionInvoker();
return controller;
} ## -69,5 +77,14 ## namespace Ninject.Web.Mvc
{
return new NinjectActionInvoker(Kernel);
}
}
///
/// Creates the action invoker.
///
/// The action invoker.
protected virtual NinjectAsyncActionInvoker
CreateAsyncActionInvoker()
{
return new NinjectAsyncActionInvoker(Kernel);
}
} } \ No newline at end of file
Remo

MvcContrib Test Helper problem

I am using MVC2 with MvcContrib HelpTester.
I have problem with testing Controllers which are in Areas.
Here is my Test class :
[TestFixture]
public class RouteTests
{
[TestFixtureSetUp]
public void Setup()
{
RouteTable.Routes.Clear();
MvcApplication.RegisterRoutes(RouteTable.Routes);
}
[Test]
public void RootMatchesHome()
{
"~/".ShouldMapTo<TradersSite.Controllers.HomeController>(x => x.Index());
}
[Test]
public void AdminProductShouldMapToIndex()
{
"~/Admin/Produit/".ShouldMapTo<TradersSite.Areas.Admin.Controllers.ProductController>(x => x.Index());
}
Here's the action Index from my ProductController in the Admin Area :
public ActionResult Index(int? page)
{
int pageSize = 10;
int startIndex = page.GetValueOrDefault() * pageSize;
var products = _productRepository.GetAllProducts()
.Skip(startIndex)
.Take(pageSize);
return View("Index", products);
}
Here is the route map in my AdminAreaRefistration :
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
Finally here is the message I get back from MbUnit :
[fixture-setup] success
[failure] RouteTests.AdminProductShouldMapToIndex
TestCase 'RouteTests.AdminProductShouldMapToIndex' failed: Expected Product but was Admin
MvcContrib.TestHelper.AssertionException
Message: Expected Product but was Admin
Source: MvcContrib.TestHelper
StackTrace:
RouteTests.cs(44,0): at CBL.Traders.ControllerTests.RouteTests.AdminProductShouldMapToIndex()
Your area routes aren't being registered in the setup. Since you're just calling RegisterRoutes, which (by default) doesn't register areas, it's getting missed.
You can either figure a way to call AreaRegistration.RegisterAllAreas() directly (which typically gets called on app start, or you need to manually register each area you want to test. In your case, the following would work:
public void Setup()
{
RouteTable.Routes.Clear();
var adminArea = new AdminAreaRegistration();
var context = new AreaRegistrationContext("Default", RouteTable.Routes);
adminArea.RegisterArea(context);
MvcApplication.RegisterRoutes(RouteTable.Routes);
}

Resources