Global functions in razor view engine - c#

I want to have a global method like w in my razor view engine for localization my MVC application. I tried
#functions{
public string w(string message)
{
return VCBox.Helpers.Localization.w(message);
}
}
but I should have this in my every razor pages and I don't want that. I want to know how can I have a global function that can be used in every pages of my project?

You can extend the HtmlHelper:
Extensions:
public static class HtmlHelperExtensions
{
public static MvcHtmlString W(this HtmlHelper htmlHelper, string message)
{
return VCBox.Helpers.Localization.w(message);
}
}
Cshtml:
#Html.W("message")

How about an extension method:
namespace System
{
public static class Extensions
{
public static string w(this string message)
{
return VCBox.Helpers.Localization.w(message);
}
}
}
Called like so:
"mymessage".w();
Or:
string mymessage = "mymessage";
mymessage.w();
Or:
Extensions.w("mymessage");

Related

Extension Method for Default HtmlHelper in RazorPage (ASP NET CORE)

Is it possible to use extension method for TextboxFor,DropdownlistFor etc in NET Core 6?
Most of the example i've seen only had the solution for NET MVC. Since net mvc use System.Web.MVC instead of Microsoft.AspNetCore.Mvc, i have no idea on how to implement it.
This is the extension method:
public static class InputDisabler
{
public static HtmlString DisableInput(this IHtmlHelper htmlString, Func<bool> expression)
{
if (expression.Invoke())
{
string formattedHtml = htmlString.ToString();
int index = formattedHtml.IndexOf('>');
formattedHtml = formattedHtml.Insert(index, " disabled=\"disabled\"");
return new HtmlString(formattedHtml);
}
return new HtmlString(htmlString.ToString());
}
}
But this extension applied as a custom helper instead of extension of default ones.
What i want:
#Html.DropdownListFor(x => x.ListData).DisableInput(() => Model.CurrentUser != Model.CreatedBy)
Is there anyway on how to solve this? I'm still learning about these things in C# so any advice is helpful.
Try to change this IHtmlHelper to this IHtmlContent in your extension method:
public static class InputDisabler
{
public static HtmlString DisableInput(this IHtmlContent htmlString, Func<bool> expression)
{
if (expression.Invoke())
{
string formattedHtml = htmlString.ToString();
int index = formattedHtml.IndexOf('>');
formattedHtml = formattedHtml.Insert(index, " disabled=\"disabled\"");
return new HtmlString(formattedHtml);
}
return new HtmlString(htmlString.ToString());
}
}
DropdownListFor returns IHtmlContent so you need to do this to be able to chain them.

Get user manager at extension class

I'm migrating my ASP.net MVC project to core version. I have an extension class with method, that returns user's name by user id (Guid).
public static class IdentityHelpers
{
public static MvcHtmlString GetUserName(this HtmlHelper html, string id)
{
var manager = HttpContext.Current
.GetOwinContext().GetUserManager<AppUserManager>();
return new MvcHtmlString(manager.FindByIdAsync(id).Result.UserName);
}
}
As I'm rewriting this to .NET Core, I don't know how to get user manager instance here. Normally I would just inject this through DI, but I don't know what to do as I'm using extension method so I can't inject it.
How can I get UserManager in static class?
Things have changed in the new version. Access the current HttpContext via the HtmlHelper.ViewContext and from there you should be able to get access to an IServiceProvider that can be used to resolve services.
public static class IdentityHelpers {
public static MvcHtmlString GetUserName(this HtmlHelper html, string id) {
HttpContext context = html.ViewContext.HttpContext;
IServiceProvider services = context.RequestServices;
var manager = services.GetService<AppUserManager>();
return new MvcHtmlString(manager.FindByIdAsync(id).Result.UserName);
}
}

Use .net core dependencies in extension methods

I have created extension methods for IUrlHelper.
public static class UrlHelperExtensions
{
public static string JavaScript(this IUrlHelper helper, string contentPath, IOptions<TypeScriptOptions> tsOptions)
{
if (tsOptions.Value != null && tsOptions.Value.Minify)
{
contentPath = Path.ChangeExtension(contentPath, ".min.js");
}
return helper.Content(contentPath);
}
public static string Css(this IUrlHelper helper, string contentPath, IOptions<LessOptions> lessOptions)
{
if (lessOptions.Value != null && lessOptions.Value.Minify)
{
contentPath = Path.ChangeExtension(contentPath, ".min.css");
}
return helper.Content(contentPath);
}
}
I would like to pass IOptions<TypeScriptOptions> tsOptions and IOptions<LessOptions> lessOptions to the methods using .NET Core's dependency injection.
In a Razor view I have the following:
#inject IOptions<CssOptions> lessOptions
<link href="#Url.Css("~/css/site.css", lessOptions)" rel="stylesheet" asp-append-version="true">
But I would simply like to do:
<link href="#Url.Css("~/css/site.css")" rel="stylesheet" asp-append-version="true">
I've tried looking at the .NET Core docs and I've done a few Google searches but I can't seem to find a way to achieve what I want without resorting to Tag Helpers which isn't something I want to do.
How can I get this to work?
As #Romoku said, extension methods are static methods and only take state as argument (or from static state).
You either need to keep using the strategy you have, passing it as an argument. Or drop the idea of extension method and create some helper class or service that gets resolved via DI:
public class UrlHelperService
{
private IOptions<CssOptions> _cssOptions;
private IOptions<JavaScriptOptions> _jsOptions;
private IUrlHelper _urlHelper;
public UrlHelperService(
IOptions<CssOptions> cssOptions,
IOptions<JavaScriptOptions> jsOptions,
IUrlHelper urlHelper)
{
_cssOptions = cssOptions;
_jsOptions = jsOptions;
_urlHelper = urlHelper;
}
public string JavaScript(string contentPath)
{
if (_jsOptions.Value != null && _jsOptions.Value.Minify)
{
contentPath = Path.ChangeExtension(contentPath, ".min.js");
}
return _urlHelper.Content(contentPath);
}
public string Css(string contentPath)
{
if (_cssOptions.Value != null && _cssOptions.Value.Minify)
{
contentPath = Path.ChangeExtension(contentPath, ".min.css");
}
return _urlHelper.Content(contentPath);
}
}
The container needs this class registered, e.g:
services.AddScoped<UrlHelperService>()
Or whatever lifecycle this type should have.
And the service would be injected in your view instead of the options instances:
#inject UrlHelperService urlHelperService
<link href="#urlHelperService.Css("~/css/site.css")" rel="stylesheet" asp-append-version="true">

Unit testing non-public methods in dotnet core controller

Suppose in an MVC5 controller I had a method in my controller that gets called by other methods in the controller, but I don't want it available to a user. If I wanted to be able to mock it, it would look like this:
[ChildActionOnly]
public virtual string DoSpecialFormatting(string mySpecialString)
{
// stuff
}
Or I could have tossed [assembly: InternalsVisibleTo("MyLittleProject.Tests")] and [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] (for Moq) into AssemblyInfo.cs and marked the method as internal instead of public:
internal virtual string DoSpecialFormatting(string mySpecialString)
{
// stuff
}
Now that there is no ChildActionOnly and I don't see an AssemblyInfo.cs file in my new ASP.NET Core project, how would I have methods in my controller class which web users cannot access but can still be mocked?
In ASP.NET Core the attribute is called NonActionAttribute.
[NonAction]
public virtual string DoSpecialFormatting(string mySpecialString)
{
// stuff
}
Imho its better than internal.
You can extract that method to a class , i.e. named SpecialFormatter, and inject to the controller via DI. To test your controller you can mock this class.
class SpecialFormatter
{
public string DoSpecialFormatting(string mySpecialString)
{
// stuff
}
}
Then in your controller
class SomeController : Controller
{
private SpecialFormatter _formatter;
public SomeController(SpecialFormatter formatter)
{
_formatter = formatter;
}
public ActionResult SomeAction(string input)
{
string output = _formatter.DoSpecialFormatting(input);
// stuff
}
}

Reusing ViewPage/HtmlHelper in seperate project in ASP.NET MVC using C#

I want to use the ViewPage/HtmlHelper class in the System.Web.Mvc namespace in a seperate project. I imported the relevant libraries and then tried this:
using System.Web.Mvc;
using System.Web.Mvc.Resources;
using System.Web.Mvc.Html;
public static class Display
{
public static string CheckBox()
{
ViewPage viewPage = new ViewPage();
return viewPage.Html.CheckBox("Test");
}
}
Which I call like this in another class that includes my display class:
string Checkbox = Display.CheckBox():
This compiles just fine, however when I run it I get:
System.NullReferenceException: Object
reference not set to an instance of an
object.
I simply want to use the HtmlHelper's extension methods as is, e.g: page.Html.ActionLink(), page.Html.Radionbutton() etc. How can I resolve this problem?
Are you trying to call your custom CheckBox() from a different place than the view? Please don't do that. The philosophy behind ASP.NET MVC is that your controller should prepare all data for the view, then the view should decide on how to render it.
If you redesign your method to be an extension method, you could do that:
public static class Display // class name really don't matter for extension methods
{
public static string CheckBox(this HtmlHelper html)
{
return html.CheckBox("Test");
}
}
Within the view:
<%= Html.CheckBox() %>
Note that this may cause a naming conflict with existing extension methods. One way to avoid that is to design something like:
New code in view:
<%= Html.Display().CheckBox() %>
New extension code:
public static DisplayExtension
{
public static Display(this HtmlHelper html)
{
return new Display(html);
}
}
public class Display // no longer static
{
private readonly HtmlHelper html;
public string Display(HtmlHelper html)
{
this.html = html;
}
public string CheckBox()
{
return html.CheckBox("Test");
}
}
The Html helpers require that the ViewContext property of the ViewPage is set. Typically, this is not the case within a controller or other class code.
Could you package this CheckBox within an ASCX file and reference it there by other views with a Html.RenderPartial method call?

Categories