Using custom html helpers in an ASP.NET MVC 3 Application - c#

So, for some hours now, I have been trying to do something that I thought - and still think - should be trivial. Basically, I created a Html helper that I needed to use to apply some CSS attribute to the selected menu of an ASP.NET MVC 3 Application. Here is my Html helper:
namespace MVCUI.Extensibility
{
public static class HtmlHelpers
{
public static MvcHtmlString MenuLink(
this HtmlHelper helper,
string text,
string action,
string controller,
string selectedCssClass,
object routeValues,
object htmlAttributes)
{
var attributes = new RouteValueDictionary(htmlAttributes);
if (!String.IsNullOrWhiteSpace(selectedCssClass))
{
var contextController = helper.ViewContext.RouteData.Values["controller"] as String ?? "Home";
var contextAction = helper.ViewContext.RouteData.Values["action"] as String ?? "Index";
if (String.Compare(
String.Format("{0}/{1}", controller, action),
String.Format("{0}/{1}", contextController, contextAction), true) == 0)
{
var cssValue = String.Empty;
if (attributes.ContainsKey("class"))
cssValue = attributes["class"] as String ?? String.Empty;
cssValue = cssValue.Trim();
if (cssValue.Length > 0)
cssValue = cssValue += (" " + selectedCssClass);
else
cssValue = selectedCssClass;
attributes["class"] = cssValue;
}
}
return helper.ActionLink(text, action, controller, new RouteValueDictionary(routeValues), attributes);
}
}
}
Here is how I am using it from a _Layout.cshtml file:
#Html.MenuLink("Posts", "Posts", "Post", "selected", new { }, new { })
For some really odd reason, I keep getting the error:
Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.
Compiler Error Message: CS1061: 'System.Web.Mvc.HtmlHelper' does not contain a definition for 'MenuLink' and no extension method 'MenuLink' accepting a first argument of type 'System.Web.Mvc.HtmlHelper' could be found (are you missing a using directive or an assembly reference?)
Here is what I have tried:
Added <add namespace="MVCUI.Extensibility" /> to <system.web.webPages.razor>/<pages>/<namespaces> section of the Web.config file at the root of the Views folder.
Added #using MVCUI.Extensibility; at the top of the _Layout.cshtml file.
Tried all combinations of (1) and (2) above.
Tried the syntax: #{ Html.MenuLink("Posts", "Posts", "Post", "selected", new { }, new { }); }
Googled, alot! All materials and resources, including our very own stackoverflow, seem to suggest that am doing the right thing.
Even tried setting [assembly: ComVisible(true)] in AssemblyInfo.cs. Well, just in case! ;)
Disclaimer: This is the first time I am trying out a html helper in an ASP.NET MVC 3 application.
Where could I be going wrong? Thanks people.

Like someone pointed out in the comments, there was something different about my setup. In my solution, I had changed the Output path for my projects - including the MVC project - from the default bin\ to something like ..\Library\Build\. No crime there since that setup has worked fine so far.
But, that is what got me into trouble. After I restored the default output path and rebuilt my project the Html helper worked. It continued to work even after I restored back my preferred output path - obviously because the dll in the bin folder got updated.
This would mean that the statement #using MVCUI.Extensibility; in my .cshtml file and <add namespace="MVCUI.Extensibility" /> in the Web.config were referencing an old outdated dll in the bin folder that didn't have the HtmlHelper defined. This bothers still. How would I have them reference the dll in my desired output path?
Anyway, I just thought I should post about the experience and the lessons just in case other people find themselves in similar trouble. Thanks people.

This would happen if the file with the extension method says using System.Web.WebPages (which has its own separate HtmlHelper class) rather than using System.Web.Mvc.

Related

Razor Pages override route getting RoutePatternException despite seemingly correct

I am using Razor Pages, and everything has been going smoothly so far.
Now I wish to create a page with an override route. Like the override routes that are shown possible here.
I am, however encountering the following exception, despite I don't seem to have the issue in my code that it describes:
RoutePatternException: There is an incomplete parameter in the route template. Check that each '{' character has a matching '}' character.
I must be somehow misunderstanding how this routing works, but I haven't been able to find someone encountering the same issue in my preliminary searches.
This is my entire code on this page so far:
#page "/layouts/{layoutId:int}/save/{revisionId:int}"
#model Project.Web.Pages.TenantBased.Layouts.SavePageModel
#{
Layout = "_TenantLayout";
ViewData["Title"] = "Title";
}
And this is the code-behind:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace Legalit.Web.Pages.TenantBased.Layouts
{
public class SavePageModel : PageModel
{
public void OnGet(int layoutId, int revisionId)
{
}
}
}
This gives the following exception when running the project:
RoutePatternException: There is an incomplete parameter in the route template. Check that each '{' character has a matching '}' character.
If I remove the first / from the route as so:
#page "layouts/{layoutId:int}/save/{revisionId:int}"
Then it stops generating the exception, but I of course get the wrong routing from it. Now my page is reachable by the directory path with this route added to the end of it.
I am using .NET 6.0.
The project type is Microsoft.NET.Sdk.Web
The project had a custom Convention in the Program.cs that was specified under the AddRazorPagesOptions configuration method.
After removing this, it worked fine.

ASP.Net MVC dynamic script bundle does not load and gives 404 error first few times

I am working on an existing ASP.Net MVC web application on .Net 4.5.2 and MVC 5.2. This has dynamic script bundles on 250 odd views which are added to the BundleTable only when the particular view is first accessed. This leads to an issue where the first time any view is hit, the script bundle returns a 404 error. But refreshing the page few times resolves the issue - at which point the bundle loads up fine and then gets cached on the browser.
The code for this looks like:
//View:
#model XYZ
#{ ViewBag.Title = "Title"; }
#section JavaScript {
#Html.RenderScripts("~/Scripts/a.js", "~/Scripts/b.js")
}
// HtmlHelper RenderScripts extension
public static IHtmlString RenderScripts(this HtmlHelper helper, bool isMinified, params string[] filePaths)
{
var path = (helper.ViewDataContainer as WebPageBase).VirtualPath.Replace("~/", "~/Scripts/").Replace(".cshtml", "").Replace("../", "");
var bundle = new ScriptBundle(path).Include(filePaths);
bundle.Orderer = _orderer;
if (!isMinified) bundle.Transforms.Clear();
BundleTable.Bundles.Add(bundle);
return Scripts.Render(path);
}
It appears that the 1st request might be adding the bundle, but there's a lag in generating and serving up the bundle itself which is resulting in the 404 error. So how soon can a bundle be used once it's added - does it need a warm up?
Is this a wrong pattern of using Script bundles? Is there a way to address this so I can continue to declare the view-specific script bundles on the view without moving them to the bundle config?

CSHTML files not loading outside of Views

I have a C# MVC Razor site. Typically, Controllers load views from the Views folder. However, I have a special circumstance where I need to render a view outside of the Views folder. How do I do that?
Controller will load /Views/Random/Index.cshtml
Can't load /Random/Index.cshtml
/Random/test.aspx loads with no issues, but can't change cshtml files to aspx files, they need to be built regularly.
I have tried return Redirect("/Random/Index.cshtml") in the Controller, and currently have no controller at all.
The weird thing is it works on my Production environment, but not in localhost. In localhost I get:
The type of page you have requested is not served because it has been explicitly forbidden. The extension '.cshtml' may be incorrect. Please review the URL below and make sure that it is spelled correctly.
Requested URL: /Random/Index.cshtml
You can definitely do this. For doing this you need to create one new custom view engine like
public class MyViewEngine : RazorViewEngine
{
private static string[] AdditionalViewLocations = new[]{
"~/Random/{0}.cshtml"
};
public MyViewEngine()
{
base.PartialViewLocationFormats = base.PartialViewLocationFormats.Union(AdditionalViewLocations).ToArray();
base.ViewLocationFormats = base.ViewLocationFormats.Union(AdditionalViewLocations).ToArray();
base.MasterLocationFormats = base.MasterLocationFormats.Union(AdditionalViewLocations).ToArray();
}
}
Then in you global.asax's Application_Start method register this view engine like this-
ViewEngines.Engines.Add(new MyViewEngine ());
If you want your viewengine to take precedence then insert this at 0th position. like this -
ViewEngines.Engines.Insert(0, new MyViewEngine());
return View("~/AnotherFolder/Index.cshtml")` should work for you.
Do not forget to indicate the Layout in your index view:
#{
Layout="~/Views/Shared/Layout.cshtml";
}

Why do I get a 404 for any new Action method in my controller?

In an ASP.NET MVC 4 project, I've added an MVC Controller with scaffolding from an EF class. I.e, CRUD operations. They all work. If I add action methods by hand, add an Html.ActionLink() to a view pointing to them, I get 404 errors.
For example, in my controller I add the AddImage method:
public ActionResult AddImage(int id)
{
var car = db.Cars.Find(id);
return View("AddImage",car);
}
This just returns a view to add images associated with the Car object. In the corresponding Index.cshtml, I add:
#Ajax.ActionLink("Add Image", "AddImage", new { id = item.CarId }, new AjaxOptions { UpdateTargetId = "modal",OnSuccess="showDialog" })
When it renders, in the console, I see a 404 when that link is clicked. Another weird thing is that when I run in the debugger, a breakpoint set in the method is hollow with a little warning icon, says no executable code is associated with this line.
I have added nothing to the RouteConfig: it just has the Default, which should work. I had an overload with an HttpPost attribute, but even without that, I still get a 404.
In previous projects, I can Action methods with impunity and they all work. So what is the problem here?
Please help, Stackoverflow, you're my only hope.
With the help of David in the comments, I think I figured out what I did to get my project in a weird state. To be brief: I added stuff to my model including adding a migration without calling Update-Database. I tried running the app without the database being up to date.
I wasn't getting an error that said as much, but after restarting VS, deleting the bin and obj folders for the project, changing the output path to just bin/ instead of bin/Debug (found on many Stackoverflow questions), I finally got the right error message.
Then I ran my update, et voila! Working project.
write this
public ActionResult AddImage(int id)
{
var car = db.Cars.Find(id);
return View(car);
}
and your link must be 'AddImage' without empty space
#Ajax.ActionLink("AddImage", "AddImage", new { id = item.CarId }, new AjaxOptions { UpdateTargetId = "modal",OnSuccess="showDialog" })

MVC4 URLHelper working, but HTMLHelper is not. What am I doing wrong

In a previous post I asked a question about getting started with helpers. I was successful but when I tried to use the technique 1. To write a different helper based on Html.RenderAction, and 2. To pass in my own custom helper I got errors once they were exported to App_Code.
Again, to emphasise, they work inline, but not when exported to App_Code.
Here is the original code:
Many parts of my code have only the following:
<section class="Box">
#{ Html.RenderAction("PageOne"); }
</section>
Many other parts have this:
#if (#Model.PageTwo)
{
<section class="Box">
#{ Html.RenderAction("PageTwo"); }
</section>
}
So my first step was to extract out into an inline helper the following which could be used in all of my code blocks above:
#helper Item(HtmlHelper html, string action, string htmlClass)
{
<section class="#htmlClass">
#{ html.RenderAction(action); }
</section>
}
The helper above allows me to replace all the code blocks that look like the first code segment above with this line:
#Item(Html, "PageOne", "Box")
Then I went on to write the second helper which looks like this:
#helper Item(HtmlHelper html, string action, string htmlClass, bool test)
{
if (test)
{
#Item(html, action, htmlClass)
}
}
This helper allows me to replace all the code blocks that look like the second code segment above with this line:
#Item(Html, "PageTwo", "Box", #Model.ShowThisTorF)
My main question once again is, this works inline, so why not when I remove it to App_Code.
Once I move it to App_Code I get the following errors:
The first problem is regarding adding a using reference (because HtmlHelper is ambiguous) to which I add the following line of code:
#using HtmlHelper=System.Web.Mvc.HtmlHelper
This removes the first error but then I get another error:
System.Web.Mvc.HtmlHelper does not contain a definition for
'RenderAction' and no extension method 'RenderAction' accepting a
first argument of type 'System.Web.Mvc.HtmlHelper' could be found (are
you missing a using directive or an assembly reference?)
I have also tried the other reference but with no result:
#using HtmlHelper=System.Web.WebPages.Html.HtmlHelper
Another problem that I am having is that I don't think the second block will work once I get the first one working. Even though it worked fine inline.
Also, I know its obvious, but if I don't say it here, someone will ask it in their answer. When I moved it out to the file App_Code, I did indeed add the file name prefix as required so the one line lumps of code looked something like:
#Helpers.Item(Html, "PageOne", "Box")
#Helpers.Item(Html, "PageTwo", "Box", #Model.ShowThisTorF)
Thanks for any help with this.
The correct HtmlHelper inside helpers in the App_Code directory is the System.Web.Mvc.HtmlHelper.
Because RenderAction is an extension method you need to add a using for the namespace where it is declared which is #using System.Web.Mvc.Html
So this should work assuming your file is named Helpers.cshtml and in the App_Code directory:
#using HtmlHelper=System.Web.Mvc.HtmlHelper
#using System.Web.Mvc.Html
#helper Item(HtmlHelper html, string action, string htmlClass)
{
<section class="#htmlClass">
#{ html.RenderAction(action); }
</section>
}
#helper Item(HtmlHelper html, string action, string htmlClass, bool test)
{
if (test)
{
#Item(html, action, htmlClass)
}
}
And the usage:
#Helpers.Item(Html, "PageOne", "Box")
#Helpers.Item(Html, "PageTwo", "Box", #Model.ShowThisTorF)

Categories