So, I read all about the new localization system in ASP.Net Core (3.1) and successfully made use of the IStringLocalizer<MyController> and the IViewLocalizer<MyView>. I also could use the localization for the [DisplayName("Property description") in Models.
Below what I seem unable to do:
In good old .Net Framework I could do this:
public class Month
{
public int MonthNumber { get; set; }
public string Name
{
get
{
switch(MonthNumber)
{
case 1:
return Properties.Resources.Jan;
case 2:
return Properties.Resources.Feb;
default:
return "?";
}
}
}
But how can I do this in a Model in ASP.Net Core 3.1?
I solved it thus:
Just add the resource files following the structure you have chosen. All docs and tutorials suggest you take a folder named "Resources" as your base folder, so that is what you see here.
The link that is most probable to survive over time that explains how to use resources in an ASP.Net Core project:
Microsoft docs on Localization for ASP.Net Core
Make sure that you mark all three "Month" resx files as Public:
Visual Studio will at first complain with the message:
Custom tool PublicResXFileCodeGenerator failed to produce an output for input file 'Month.en.resx' but did not log a specific error.
Simply get rid of this error by restarting Visual Studio!
Now you can use the resources as follows:
public string Name
{
get
{
return MonthNumber switch
{
1 => Resources.Models.Month.Jan,
2 => Resources.Models.Month.Feb,
_ => "?"
};
}
}
You need to inject IStringLocalizer to the class:
public class Month
{
public int MonthNumber { get; set; }
private readonly IStringLocalizer Localizer;
public Month(IStringLocalizer localizer)
{
Localizer = localizer;
}
public string Name
{
get
{
switch(MonthNumber)
{
case 1:
return Localizer["Jan"];
case 2:
return Localizer["Feb"]
default:
return "?";
}
}
}
}
Another approach can be done by adding localized month names for the numbers in the resource file, so:
var monthName = Localizer["4"];
// result: April for English culture
// or Nisan for Turkish culture
Just for clarification;
The resource key can have three types of access modifiers:
Internal
Public
No code generation
If the key is marked with Internal or Public you can access it as you mentioned, because the compiler will auto generate a static class .cs linked to the relevant resource file with the key names as accessible properties.
But the common approach with Asp.Net Core is to work with Shared resources, and shared resource has the access modifier as No code generation; so thats mean no peoperty keys will be generated at the backend (.cs will not be generated). And in this case you have to inject the IStringLocalizer or whatever locaizer in use to the class.
So changing the key access modifier to Internal or Public can work as well, but it is not a best practice ;)
I want to create my own custom HTML Helper like the ones used in ASP.NET MVC, but I haven't been able to find how to implement them in the correct way.
I have found how to create custom Tag Helpers but not HTML Helpers. How do I create my own custom HTML Helpers?
For me I thought my HTML helpers weren't working until I spotted that the extension method is now on IHtmlHelper not HtmlHelper.
So for .net core:
public static IHtmlContent CheckboxListFor<TModel>(this IHtmlHelper<TModel> html,
Expression<Func<TModel, List<CheckboxListItem>>> expression) ...
Instead of for .net:
public static HtmlString CheckboxListFor<TModel>(this HtmlHelper<TModel> html,
Expression<Func<TModel, List<CheckboxListItem>>> expression) ...
EDIT: I've also updated the return type for .net core to be IHtmlContent as using something like HtmlContentBuilder is a nicer way to compose HTML content and returning that returns IHtmlContent
HTML Helpers look to be supported in ASP.NET Core and are awaiting documentation:
https://learn.microsoft.com/en-au/aspnet/core/mvc/views/html-helpers
[Edit:] Since answering, the above page no longer exists. I'd say HTML Helpers, while they work, are no longer "supported" in ASP.NET Core.
Looking at the ASP.NET Core source they work fairly similarly to older versions of ASP.NET MVC:
https://github.com/aspnet/AspNetCore/blob/master/src/Mvc/Mvc.ViewFeatures/src/Rendering/HtmlHelperDisplayExtensions.cs
Example
MyHTMLHelpers.cs:
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
using System;
namespace MyApp.Helpers
{
public static class MyHTMLHelpers
{
public static IHtmlContent HelloWorldHTMLString(this IHtmlHelper htmlHelper)
=> new HtmlString("<strong>Hello World</strong>");
public static String HelloWorldString(this IHtmlHelper htmlHelper)
=> "<strong>Hello World</strong>";
}
}
_ViewImports.cshtml (second line is the important change):
#using MyApp
#using MyApp.Helpers
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
MyView.cshtml:
<div>#Html.HelloWorldHTMLString()</div>
<div>#Html.HelloWorldString()</div>
Outputs:
Hello World
<strong>Hello World</strong>
Here is an example for .Net Core 2 using TagBuilders
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
using System.IO;
public static IHtmlContent HelloWorld(this IHtmlHelper html, string name)
{
var span = new TagBuilder("span");
span.InnerHtml.Append("Hello, " + name + "!");
var br = new TagBuilder("br") {TagRenderMode = TagRenderMode.SelfClosing};
string result;
using (var writer = new StringWriter())
{
span.WriteTo(writer, System.Text.Encodings.Web.HtmlEncoder.Default);
br.WriteTo(writer, System.Text.Encodings.Web.HtmlEncoder.Default);
result = writer.ToString();
}
return new HtmlString(result);
}
I was never able to get HtmlHelper extension methods to work, I always recieved:
'IHtmlHelper' does not contain a definition for 'MethodName' and no extension method 'MethodName' accepting a first argument of type 'IHtmlHelper' could be found (are you missing a using directive or an assembly reference?)
Even though I had the proper namespace in my _ViewImports.cshtml file. So I decided to use the ability of Razor pages to now support injecting services that have been registered for dependency injection. As an example I have the need to inject some values from my configuration file into my _Layout.cshtml file. So I did the following:
1) Defined a IConfigurationHelperService interface:
public interface IConfigurationHelperService
{
string GetApiUrl();
}
2) Defined an implementation of that interface in a ConfigurationHelperSerivce class (which itself is using dependency injection to get the regular configuration class):
public class ConfigurationHelperService : IConfigurationHelperService
{
public ConfigurationHelperService(IConfiguration configuration)
{
Configuration = configuration;
}
private IConfiguration Configuration { get; }
public string GetApiUrl()
{
return GetConfigurationValue(ApiUrl);
}
private string GetConfigurationValue(string key)
{
var value = Configuration[key];
if (value.IsNullOrEmpty()) throw new KeyNotFoundException($"Configruation does not contain an instance of {key}");
return value;
}
}
3) Registered the service for injection via ConfigureServices in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfigurationHelperService, ConfigurationHelperService>();
services.AddMvc();
}
4) Added the proper namespace as a using statement into my _ViewImports.cshtml file.
5) Used the #inject keyword to define it for use in the _Layout.cshtml file.
#inject IConfigurationHelperService ConfigHelper
<!DOCTYPE html>
<html>
...
#ConfigHelper.GetApiUrl()
...
</html>
It worked great for me, and I can see a lot more uses for this on simpler pages where defining models would be too much work.
Well i guess this answer won't be noticed but here's what i came up with using service registrations:
I hope it helps someone.
Register the service:
services.AddTransient<IHtmlHelperFactory, HtmlHelperFactory>();
Use the service:
var helper = HttpContext.RequestServices.GetRequiredService<IHtmlHelperFactory>().Create();
Interface:
public interface IHtmlHelperFactory
{
IHtmlHelper Create();
}
Implementation:
public class HtmlHelperFactory : IHtmlHelperFactory
{
private readonly IHttpContextAccessor _contextAccessor;
public class FakeView : IView
{
/// <inheritdoc />
public Task RenderAsync(ViewContext context)
{
return Task.CompletedTask;
}
/// <inheritdoc />
public string Path { get; } = "View";
}
public HtmlHelperFactory(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
/// <inheritdoc />
public IHtmlHelper Create()
{
var modelMetadataProvider = _contextAccessor.HttpContext.RequestServices.GetRequiredService<IModelMetadataProvider>();
var tempDataProvider = _contextAccessor.HttpContext.RequestServices.GetRequiredService<ITempDataProvider>();
var htmlHelper = _contextAccessor.HttpContext.RequestServices.GetRequiredService<IHtmlHelper>();
var viewContext = new ViewContext(
new ActionContext(_contextAccessor.HttpContext, _contextAccessor.HttpContext.GetRouteData(), new ControllerActionDescriptor()),
new FakeView(),
new ViewDataDictionary(modelMetadataProvider, new ModelStateDictionary()),
new TempDataDictionary(_contextAccessor.HttpContext, tempDataProvider),
TextWriter.Null,
new HtmlHelperOptions()
);
((IViewContextAware)htmlHelper).Contextualize(viewContext);
return htmlHelper;
}
}
This has been well explained by Danny van der Kraan in his blog post here. The answer below is an extract from this post:
ASP.NET Core 1.0 [MVC 6] comes with a new exciting feature called TagHelpers. In ASP.Net Core 1.0 there is no concept of HTML Helper like in MVC.
What are TagHelpers?
TagHelpers can be seen as the evolution of HTML helpers which were introduced with the launch of the first MVC framework. To provide context you have to imagine that with classic ASP the only way you could automate the generation of HTML is via custom subroutines. After that ASP.NET came with server controls, with view states as biggest plus, to simulate the look and feel of desktop applications and help with the transition for desktop developers. But we all know what happens when we try to jam squares in to round holes. We had to face the fact that web development is nothing like desktop development. To get in line with proper web development the ASP.NET MVC framework was launched with HTML helpers to automate the HTML output. But HTML helpers never really gelled, especially not with front end developers and designers. One of the main pet peeves was that it made you switch a lot from angle brackets (HTML, CSS) to C# (Razor syntax) during work on views, which made the experience unnecessarily uncomfortable. [MVC 6] wants to address this and some smaller issues by introducing TagHelpers. Example
HTML helper:
#Html.ActionLink(”Home”, ”Index”, ”Home”)
With the anchor TagHelper this would look like:
<a asp-action="Index" asp-controller="Home">Home</a>
PS: Please note that asp- is just a convention, but more on that later.
The output rendered in the browser is the same for both:
Home
PS: Provided the default route has not been altered.
For more information about TagHelpers click here
To create a custom HTML helper you have create a static class and static method.
below example is for a custom HTML helper for submit button.
namespace TagHelpers.Helpers
{
public static class CustomHtmlHelpers
{
public static IHtmlContent SubmitButton(this IHtmlHelper htmlHelper, string value, string name )
{
string str = "<input type='submit' value ='"+ value +"'name='"+ name +"' />";
return new HtmlString(str);
}
}
}
Make sure you add below using statements.
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
To access the helper everywhere on the page you need to add the namespace in to the viewimports.cshtml file
#using TagHelpers.Helpers
Now, You can now use it on the page where you want to define a button.
<div>
#Html.SubmitButton("Login", "Command")
#Html.SubmitButton("Cancel", "Command")
</div>
Here is an example to get Enum name based on the Enum value in view. Custom HTML Helper for Enum Type
public static IHtmlContent DisplayEnumFor(this IHtmlHelper htmlHelper, string value, Type enumType)
{
if (htmlHelper == null)
throw new ArgumentNullException(nameof(htmlHelper));
if (value == null)
throw new ArgumentNullException(nameof(value));
if (!enumType.IsEnum)
throw new ArgumentException("Type must be an enumerated type");
foreach (var item in Enum.GetValues(enumType))
if (((int)item).ToString().Equals(value.Trim()))
return new HtmlString(item.ToString());
return new HtmlString(value);
}
Kept it simple but renders as expected. Make sure you have the right attributes set for the right elements. Please add suggestions if needs improvement or give your votes if it looks good.
public static class IconExtensions
{
public static IHtmlContent CCIcon(string iconName, string? toolTip = null)
{
return new HtmlString($"<img src=\"/img/{iconName}.png\" alt=\"{iconName}\" class=\"img-ca-annexicons\" title=\"{toolTip??iconName}\" />");
}
}
I am trying to write a List of a specific type using Kryo serializer but I am getting errors when I try to read/write it. I am using source provided by apache spark for KryoRegistrator
List I am trying to write is of type List<A> which could be an ArrayList or any other type of list
Code
Class FakeRegsitrator implements KryoRegistrator{
#Override
public void registerClasses(Kryo kryo) {
CollectionSerializer listSerializer = new CollectionSerializer();
listSerializer.setElementClass(A.class, kryo.getSerializer(A.class));
listSerializer.setElementsCanBeNull(false);
kryo.register(A.class, new Serializer<A>(true, true) {
public void write(Kryo kryo, Output output, A a) {
output.writeLong(a.getFieldA)
output.WriteString(a.getFieldB)
}
public A read(Kryo kryo, Input input, Class type) {
return new A(input.readLong(), input.readString())
}
}
}
What am I missing here?
I was able to get it working by registering Arraylist.class
code :
kryo.register(ArrayList.class);
in read method use
kryo.readObject(input, ArrayList.class);
in write method use
kryo.writeObject(output, entry.getArchivePortions());
I'm developing with Silverlight 4 and Prism 4.
I'm also using Unity as my injection container.
I'm trying to create the module catalog from xaml, but I get this error "IModuleCatalog does not contain a definition of CreateFromXaml...".
My code snippet is:
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Practices.Prism.UnityExtensions;
using Microsoft.Practices.ServiceLocation;
using Microsoft.Practices.Prism.Modularity;
using Microsoft.Practices.Prism.MefExtensions;
namespace MyModularityProject {
public class MyBootStrapper : UnityBootstrapper {
protected override DependencyObject CreateShell() {
return ServiceLocator.Current.GetInstance<Shell>();
}
protected override void InitializeShell() {
base.InitializeShell();
Application.Current.RootVisual = (UIElement)Shell;
}
protected override IModuleCatalog CreateModuleCatalog() {
// This is the isntruction that doesn't compile
return ModuleCatalog.CreateFromXaml(new
Uri("/MyProject.Silverlight;component/ModulesCatalog.xaml",
UriKind.Relative));
}
}
}
What could I be missing here?
The reason that you need to add the full path to the ModuleCatalog type is that there is a ModuleCatalog property within the Bootstrapper base class that UnityBootstrapper inherits. If you don't qualify the name, you are essentially calling an accessor on a property which returns IModuleCatalog. The interface definition does not include this function.
Given the new dynamic support in C# 4, is it possible to write a class in such a way that if a method is invoked on an instance and that method is not present, dispatch is passed to another method? This might look something like:
public class Apple : ... {
// ...
private ... MethodMissing(string name, ...) {
if (name == "TurnIntoOrange") {
// do something
}
}
}
dynamic d = new Apple();
d.TurnIntoOrange(); // Not actually defined on Apple; will pass to MethodMissing.
Other languages would call this "method_missing support", under the more general heading of metaprogramming. I'm not sure what C# calls this specifically. But is it possible?
Absolutely. Either implement IDynamicMetaObjectProvider or derive from DynamicObject for a much simpler route. See the DLR documentation for some good examples.
Here's a quick example of DynamicObject:
using System;
using System.Dynamic;
public class MyDynamic : DynamicObject
{
public override bool TryInvokeMember
(InvokeMemberBinder binder,
object[] args,
out object result)
{
Console.WriteLine("I would have invoked: {0}",
binder.Name);
result = "dummy";
return true;
}
public string NormalMethod()
{
Console.WriteLine("In NormalMethod");
return "normal";
}
}
class Test
{
static void Main()
{
dynamic d = new MyDynamic();
Console.WriteLine(d.HelloWorld());
Console.WriteLine(d.NormalMethod());
}
}
<plug>
I have a bigger example of DynamicObject in the 2nd edition of C# in Depth but I haven't yet implemented IDyamicMetaObjectProvider. I'll do so before the book's release, but the early access edition only has the DynamicObject example at the moment. Btw, if you buy it today it's half price - use the code twtr0711. I'll edit this answer later on to remove that bit :)
</plug>