Compile error with T4MVC generated code in a MVC 3 project - asp.net-mvc

We are developing a web application with ASP.Net 4 & MVC 3 Framework. I've installed T4MVC through NuGet and all the Views, Controllers and static content are succesfully generated as strong types.
But, when I try to compile the project, it raises an error at generated file T4MVC.cs, which is:
'T4MVC_ViewResultBase.FindView(System.Web.Mvc.ControllerContext)':
return type must be 'System.Web.Mvc.ViewEngineResult' to match overridden member
'System.Web.Mvc.ViewResultBase.FindView(System.Web.Mvc.ControllerContext)'
This is the source code generated:
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public class T4MVC_ViewResultBase : System.Web.Mvc.ViewResultBase,
IT4MVCActionResult
{
public T4MVC_ViewResultBase(string area, string controller, string action):
base() {
this.InitMVCT4Result(area, controller, action);
}
protected override void FindView(System.Web.Mvc.ControllerContext context){}
public string Controller { get; set; }
public string Action { get; set; }
public RouteValueDictionary RouteValueDictionary { get; set; }
}
The error says that:
protected override void FindView(System.Web.Mvc.ControllerContext context) { }
should be:
protected override ViewEngineResult
FindView(System.Web.Mvc.ControllerContext context) { }
But then it raises another compiling error, as this method should return code.
If we check the base class it inherits from, System.Web.Mvc.ViewResultBase, it actually declares FindView() with ViewEngineResult return type:
public abstract class ViewResultBase : ActionResult
{
...
protected abstract ViewEngineResult FindView(ControllerContext context);
}
Has anyone got this error? Has it something to do with MVC version, are we are using MVC 3?
Thanks a lot!
Sergi

I think I see the problem, and it is a T4MVC bug. But hopefully it's easy to work around.
Do you have a controller action that is declared to return a ViewResultBase? If so, can you change the return type to be ActionResult? Or alternatively you can change the return type to be whatever the concrete type is that you're returning (e.g. is it ViewResult)?
The T4MVC bug is that it doesn't correctly override non-void methods in ActionResult types.

Related

Dependant object as parameter to dependency in Autofac

In broader terms what I am trying to achieve with Autofac is to pass the dependant (a.k.a. parent) object to its dependencies.
For example:
interface IDependency {}
class Dependant
{
IDependency Dependency { get; set; }
}
class ConcreteDependency : IDependency
{
ConcreteDependency(Dependant dependant) { /* ... */ }
}
I am hoping this could work, because Dependant breaks the dependency loop using property injection (meaning you can create an instance of Dependant, before having to resolve IDependency). Whilst, if both classes used ctor-injection this wouldn't be possible.
Specifically, I am trying to inject the current ASP.NET MVC controller instance to one of its dependencies.
Take a look at:
public abstract class ApplicationController : Controller
{
public ILogger Logger { get; set;}
}
public class SomeController : ApplicationController
{
[HttpPost]
public ActionResult Create(FormCollection formData)
{
// something fails...
this.Logger.Log("Something has failed.");
}
}
public interface ILogger
{
public void Log(string message);
}
public class TempDataLogger : ILogger
{
private ControllerBase controller;
public NullLogger(ControllerBase controller)
{
this.controller = controller;
}
public void Log(string message)
{
this.controller.TempData["Log"] = message;
}
}
In plain English the above code uses TempData as a way of "logging" messages (maybe to print it out in a nice way in view-layout or something...).
Simple enough all controllers are registered in Autofac:
builder.RegisterControllers(typeof(MvcApplication).Assembly)
.PropertiesAutowired(); // not strictly necessary
But then, how can I tweak the ILogger registration below to make it work?
builder.RegisterType<TempDataLogger>()
.As<ILogger>()
.InstancePerRequest();
Is this even possible in Autofac?
Thank you.
In case anyone else is interested, the solution below is the closest I was able to get so far:
builder.RegisterControllers(typeof(MvcApplication).Assembly)
.PropertiesAutowired() // not strictly necessary
.OnActivating(e => ((ApplicationController)e.Instance).Logger = new TempDataLogger((ApplicationController)e.Instance));
... and therefore, no need to;
builder.RegisterType<TempDataLogger>()
.As<ILogger>()
.InstancePerRequest();

ASP.NET MVC 3 : How to turn determine Controller Action from Url

Surprised I'm not finding this answer anywhere, how can I determine what Controller/Action will be invoked for a given URL in MVC 3?
Update
What I really want to know:
"how can I determine what ControllerAction will be invoked for a given URL in MVC 3?" ....yeah
So, either I'm not aware of the magic method that does this:
ControllerActionInfo GetControllerActionInfo(string url)
Or, I will have to create it myself doing whatever MVC does when it gets an http request.
My purpose of asking about this on StackOverflow is that I can save some time reverse engineering this behavior. The correct answer should resemble:
Here's how you can do it: and some code would follow.
You have to use a dummy HttpContext and HttpRequest classes as follows:
public class DummyHttpRequest : HttpRequestBase {
private string mUrl;
public DummyHttpRequest(string url) {
mUrl = url;
}
public override string AppRelativeCurrentExecutionFilePath {
get {
return mUrl;
}
}
public override string PathInfo {
get {
return string.Empty;
}
}
}
public class DummyHttpContext : HttpContextBase {
private string mUrl;
public DummyHttpContext(string url) {
mUrl = url;
}
public override HttpRequestBase Request {
get {
return new DummyHttpRequest(mUrl);
}
}
}
Edit: Also, you can extend the DefaultControllerFactory and add a simple method to get the desired information instead of an instance of Controller. (Note: It's merely a sample, you have to support other aspects like ActionNameAttribute and so on)
public class ControllerActionInfo {
public ControllerActionInfo(Type controllerType, MethodInfo action) {
ControllerType = controllerType;
Action = action;
}
public Type ControllerType { get; private set; }
public MethodInfo Action { get; private set; }
}
public class DefaultControllerFactoryEx : DefaultControllerFactory {
public ControllerActionInfo GetInfo(RequestContext requestContext, string controllerName) {
Type controllerType = GetControllerType(requestContext, controllerName);
if (controllerType == null) {
return null;
}
MethodInfo actionMethod = controllerType.GetMethod(requestContext.RouteData.GetRequiredString("action"), BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public);
return new ControllerActionInfo(controllerType, actionMethod);
}
}
Then, use following code snippet to get access to the controller:
DummyHttpContext httpContext = new DummyHttpContext("~/home/index");
RouteData routeData = RouteTable.Routes.GetRouteData(httpContext);
// IController controller = new DefaultControllerFactory().CreateController(new RequestContext(httpContext, routeData), routeData.GetRequiredString("controller"));
DefaultControllerFactoryEx controllerFactory = new DefaultControllerFactoryEx();
var result = controllerFactory.GetInfo(new RequestContext(httpContext, routeData), routeData.GetRequiredString("controller"));
The logic for this is in the System.Web.Mvc.MvcHandler class, the System.Web.Mvc.DefaultControllerFactory class, and the System.Web.Mvc.ControllerActionInvoker class. .NET Reflector is your friend.
Basically, the MVC framework:
Uses reflection to get all the controllers in the application project.
Then it does something like IEnumerable<string> controllerNames = controllerTypes.Select(controllerType => controllerType.Name.Replace("Controller",string.Empty));. It then tries to match the first path segment, {controller}, to one of these sanitized controller type names (case-insensitive).
Then, it looks at this controller's public methods that have a return type that is of type ActionResult or some derivative. It matches the method name to the second path segment, {action}, as the action method to be called.
If the selected method has a parameter that is named id, then it matches the third path segment {id} to that value, and passes it to the method. Otherwise, the optional id parameter is ignored.
If the ActionResult type that is returned is a derivative of ViewResultBase then the IViewEngine tries to locate a corresponding view in the project using whatever conventions have been specified for that view engine. The WebFormViewEngine, for example, looks in the project for ~/Views/{controller}/{action}.ascx, ~/Views/{controller}/{action}.aspx, ~/Views/Shared/{action}.ascx, ~/Views/Shared/{action}.aspx by default.
If you want to further understand how routing works in MVC, I would highly suggest Scott Gu's article on MVC Routing.

ControllerContext is null and BaseController.OnActionExecuting() not called when using Html.Action

We use a BaseController to cache basic authentication information before every action executes:
public abstract class BaseController : Controller
{
protected bool IsLoggedIn { get; set; }
protected string Username { get; set; }
...
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var identity = base.User.Identity;
this.IsLoggedIn = identity.IsAuthenticated;
this.Username = identity.Name;
...
}
}
And our child controller has a actions for the main page (Index) and a partial view (GetNavigation):
[Authorize]
public partial class CollaborationController : BaseController
{
[HttpGet]
public virtual ViewResult Index()
{
var viewModel = this.MakeViewModel<FullPageViewModel>();
return this.View(MVC.Collaboration.Views.Index, viewModel);
}
[HttpGet]
public virtual PartialViewResult GetNavigation()
{
var viewModel = NavigationViewModel.Make(this.User);
return this.PartialView(MVC.Collaboration.Views.Navigation, viewModel);
}
}
And the partial view is rendered directly with Html.Action():
#Html.Action(MVC.Collaboration.GetNavigation())
Seems like it should work, but BaseController.OnActionExecuting does not get called. And I can't even call it directly because this.ControllerContext and base.User are both null. I also tried subclassing ActionFilterAttribute, but its OnActionExecuting method doesn't get called, either.
I know this is an old question but here is how I handle this. In my child controller I create the OnActionExecuting method and call the base controller from there.
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
}
At least sort-of answered my own question:
Substituting
#Html.Action("GetNavigation", "Collaboration")
for
#Html.Action(MVC.Collaboration.GetNavigation())
fixes it. MVCContrib's syntax seems to be the culprit, anyone know why? Even better, anyone know a work-around that lets me avoid those nasty, non-refactoring-safe, magic strings?

Ninject.Web.Mvc add-on not working with ASP.NET MVC 2

I'm using the Ninject.Web.Mvc (the MVC 2 version) add-on with ASP.NET MVC 2. This is an excerpt of my Global.asax.cs:
protected override void OnApplicationStarted()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes;
// RegisterAllControllersIn() is not available in the MVC 2 version of Ninject
}
protected override IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<IRepository>().To<NHibernateRepository>();
return kernel;
}
I also have a base RepositoryController:
public class RepositoryController : Controller
{
protected IRepository Repository { get; set; }
public RepositoryController()
{
}
public RepositoryController(IRepository repository)
{
Repository = repository;
}
}
So as you can see, it's a very simple setup where RepositoryController expects to be injected with an instance of an IRepository, and Ninject is configured to use a concrete instance of NHibernateRepository. However, this doesn't work and the Repository property is null whenever I try to access it in a controller. However, if I change the code to this instead:
[Inject]
public IRepository Repository { get; set; }
Then it works fine. Does anyone know why constructor injection isn't working, but property injection is?
Try removing the parameterless constructor.
Ninject might be picking the wrong constructor to resolve.
To test it out, you could put a breakpoint in both constructors and see which one fires, but I have a feeling it's the parameterless one.

ASP.NET MVC - Ninject 2.0 Activation Error

I just started working with dependency injection for the first time and I am using as Ninject 2.0 as my IoC container in an ASP.NET MVC 2 website and I'm hitting an activation error that I am not sure how to react to. I am sure it's simple so hopefully someone can point me in the right direction without too much thought.
I have a property on my class BaseController which takes an IWebsiteSettings and is flagged with the [Inject] attribute. In my StandardKernel I load a module with the following code:
public class WebModule : Module
{
public override void Load()
{
Bind<IWebsiteSettings>()
.ToProvider(new WebsiteSettingsProvider(WebConfigurationManager.AppSettings))
.InSingletonScope();
}
}
public class WebsiteSettingsProvider : Provider<WebsiteSettings>
{
private const string WebsiteNameKey = "Website.Name";
private const string ContactFormEmailSubjectKey = "ContactForm.EmailSubject";
private const string ProductImageDirectoryKey = "Products.ImageDirectory";
private const string UploadTempDirectoryKey = "Uploads.TempDirectory";
protected NameValueCollection Settings { get; set; }
public WebsiteSettingsProvider(NameValueCollection settings)
{
Settings = settings;
}
protected override WebsiteSettings CreateInstance(IContext context)
{
return new WebsiteSettings
{
WebsiteName = Settings[WebsiteNameKey] ?? string.Empty,
ContactFormEmailSubject = Settings[ContactFormEmailSubjectKey] ?? string.Empty,
ProductImageDirectory = Settings[ProductImageDirectoryKey] ?? string.Empty,
UploadsTemporaryDirectory = Settings[UploadTempDirectoryKey] ?? string.Empty
};
}
}
This is fairly straightforward- I'm trying to load some data from the web.config file and store it in a singleton object for use across my controllers. The call to Bind seems to function exactly as it should and the Settings property in my provider is correctly initialized with the AppSettings collection in the config file. Still, when the application loads the first time:
Server Error in '/' Application.
Error activating SByte* using implicit self-binding of SByte*
No constructor was available to create an instance of the implementation type.
Activation path:
4) Injection of dependency SByte* into parameter value of constructor of type string
3) Injection of dependency string into property WebsiteName of type WebsiteSettings
2) Injection of dependency IWebsiteSettings into property WebsiteSettings of type HomeController
1) Request for HomeController
Suggestions:
1) Ensure that the implementation type has a public constructor.
2) If you have implemented the Singleton pattern, use a binding with InSingletonScope() instead.
Interestingly, if I refresh the page I don't get the exception and a call to Kernel.Get() returns the correct object.
Any advice?
(We talked about this on IRC, but I'm putting it here in case someone else runs into this problem as well.)
WebsiteSettings has [Inject] attributes on its properties, so Ninject is trying to resolve a binding from System.String to inject a value into the properties. Since you're using a custom provider to activate WebsiteSettings instances, you don't need [Inject] attributes on its properties.
The offending code was actually in the class WebsiteSettings where I was doing this:
public class WebsiteSettings : IWebsiteSettings
{
[Ninject.Inject]
public string WebsiteName
{
get; set;
}
[Ninject.Inject]
public string UploadsTemporaryDirectory
{
get; set;
}
[Ninject.Inject]
public string ContactFormEmailSubject
{
get; set;
}
[Ninject.Inject]
public string ProductImageDirectory
{
get; set;
}
}
By placing the Inject attribute on my properties I was causing Ninject to try to assign values that I never bound. Because I am using a Provider to load my type I do not need to include the Inject attribute.

Resources