How to use a shared resource for localization in .net core 2 in controllers? - asp.net-core-mvc-2.0

I'm new to .net core development after years of working in .net framework on mostly webform applications.
I'm trying to localize a new project and looking at options determined for this particular use that a shared resource would be the most maintainable solution long term and followed this example: https://damienbod.com/2017/11/01/shared-localization-in-asp-net-core-mvc/
This appears to work great for adding the localized data in the view but I am struggling to be able to do so in the controller such as returning a localized error when something is caught server side and a custom message would be returned.
In my controller's I added
private readonly LocService _SharedLocalizer;
Within a view's method in the controller if I try and add
ViewBag.localizedmessage = _SharedLocalizer.GetLocalizedHtmlString("message")
I get a null error on accessing the page on this line.
If I create a new instance within the view's method I am not sure what to provide as the argument value for the IStringLocalizerFactory.
_SharedLocalizer = new LocService();
What is the piece I am missing or how do I go about properly accessing the shared resource in a controller?

Try this
public class YourController : Controller
{
private readonly LocService _SharedLocalizer;
public YourController(LocService localizer)
{
_SharedLocalizer = localizer;
}
}

Related

Conditional namespaces in mvc views

I'm working on MVC 3 and using resource files to localize the application. Now we have another customer on board for the application and they would like to change some of the text on the application..typical.
I have create a separated resource file for them and would like to do something like this in views
if (customer =A )
#using Resources.customerA
else
#using Resources.customerB
I've a resource class in both namespaces so something like this works fine if I change the namespace
Resource.WelcomeUser
Is it possible to use conditional using statement in views? I'm unable to find the right syntax for this. Any ideas?
You can put both using statements in View, but when you use classes you would have to write some namespace prefix.
Example:
#using Project.Resources.customerA
#using Project.Resources.customerB
using classes:
customerA.WelcomeUser
customerB.WelcomeUser
I think there is no other way, because two files cannot have the same path.
What you're really talking about is the provider pattern. You have two (or more) interchangeable things, and you want to be able to use one or the other contextually.
The correct way to do this in an OO context is to create and use an interface, while then injecting the actual implementation you want at runtime. You can actually achieve this in ASP.NET Core, which supports injection in Views, but in ASP.NET MVC 5 and previous, you'd need to go a little out of your way. I'm imagining these are currently static classes, since you're referencing them merely via namespace. With that approach, you'd need to follow #Ssheverdin's advice and use the FQN of the class (i.e. with the namespace):
#if (customer == A)
{
#Resources.customerA.StaticClass.Property
}
else
{
#Resources.customerB.StaticClass.Property
}
Alternatively, you could change the static classes to be instance classes and use a factory pattern to return the right one. This is a very simplistic example, but hopefully enough to convey the idea:
public static class ResourceFactory
{
public static IResourceClass GetForCustomer(string customer)
{
switch (customer)
{
case "A":
return new Resources.customerA.ResourceClass();
default:
return new Resources.customerB.ResourceClass();
}
}
Then:
#{ var resource = ResourceFactory.GetForCustomer(customer); }
I have managed to achieve the behaviour by adding a web.config file under views folder and including the namespaces there, i have to remove the #using statement from all views obviously. You might find that intellisense doesn't work anymore for you so try closing all views and reopen them again.
With this way I can create a separate web.config file for each customer and specify the relevant namespaces accordingly. Now just have to make sure to provide the RIGHT config file for each customer when deploying the release:)

Universal App Resource resw values in different views

I try to create a Universal Windows App which has a main window containing different views.
ContentFrame.Navigate(typeof(SimplePage));
where ContentFrame is a XAML Frame and SimplePage is a view.
The project has two localisations. Therefore I created a folder Strings in the solution containing two folders, en and de, containing each a Resources.resw file.
I want to use a string from the resw-file inside the SimplePage-view. Therefore I tried:
tbSimpleInput1.Text = ResourceManager.Current.MainResourceMap.GetValue("Resources/dataToolDiameter", ResourceContext.GetForCurrentView()).ValueAsString;
I also tried using ResourceContext.GetForViewIndependentUse() instead of ResourceContext.GetForCurrentView() but I always get a NullReferenceException when trying to debug.
What is the correct way to access the resources in different views?
Here a screenshot of the solution in Visual Studio:
If you're having a single-project solution, I'd recommend you either to create a Shell - as the Microsoft example recommends, or to use the App.xaml.cs class for localization purposes.
First, in the constructor of either class, get the current ResourceLoader:
// E.g use the static constructor of your App class
static App()
{
_resourceLoader = new ResourceLoader();
}
Now getting a resource (e.g. a text) is very easy:
public static string GetLocalizedString(string key)
{
return _resourceLoader.GetString(key);
}
Now you can load a string form the default resource dictionary:
tbSimpleInput1.Text = App.GetLocalizedString("dataToolDiameter");
Please note: this only works as long as you use the default pattern for localization in your project. If you use different resource files, you'd have use an overload of the ResourceLoader constructor.

MVC - How to instantiate, store and make a typed variable available throughout the application, once per page view

I am developing an MVC app to serve multiple domains - each is a branch of a larger company.
A LocalBranch class stores details such as phone, address, email, location coordinates etc.
I want to create a single instance of this class per http request and have it available throughout the application - from within controllers, views, some helper classes and other code.
Is there a recommended way of doing this?
Right now I have it as a property on a BaseController and use ViewBagto pass it to views. But I would prefer it strongly typed in Views if possible.
I don't want to put it in an application variable, because we need to serve different values to different domains.
I would rather avoid a session variable if possible because we might scale up to use multiple servers in the future, and I've heard this doesn't play well with sessions.
Please feel free to update tags / title if you think there is a clearer way of expressing what I'm after. Thank you.
The best way to maintain your state in a web application per request is simply use the HttpContext class.
You need to store your state(LocalBranch) as an Item in the HttpContext:
HttpContext.Current.Items.Add("LocalBranch", GetLocalBranch());
You can fetch the Item all across your application like this:
LocalBranch branch = HttpContext.Current.Items["LocalBranch"] as LocalBranch;
The Items property is simply a key value Dictionary. The value is an object. You will have to check for nulls and this is really similar to the Session object you know. The main difference is the scope. The HttpContext is a dot net object that has a lifetime of an http request.
Now using the HttpContext the way I've shown you is the simplest way to do it.
You can go two steps forward and use a framework called Unity and add a lifetime to your objects.
Unity does much more and the lifetime management is just one gem.
You can create a custom HttpContext lifetime that generates objects per request. Something like this.
And them all you need to do is:
1.Register you LocalBranch class with the HttpContext lifetime.
2.Add a static Current property which will use the Unity container and resolve the correct instance of LocalBranch.
3.Use it something like this: LocalBranch.Current
BTW, you can use Unity's dependency injection for injecting objects into controllers and other modules. That's a better practice then just using the static Current property.
You kind of have two questions here. The first is "How do I create a single instance of this class per HttpRequest?" The second is "How do I make this available to strongly typed views?"
The first has pretty much been answered by #amir-popovich to use dependency injection. However, FWIW I would probably use Ninject instead of Unity (just preference, really) and I would probably implement it differently. I would not use HttpContext, and simply build a service (which is instanciated using Ninject's OnePerHttpRequest Module, passing the domain as an argument to get the proper values).
Then, in order to add these LocalBranch values to your strongly typed View Model, you can first create a base view model which holds this type:
public class BaseViewModel
{
public LocalBranch Branch {get;set;}
}
Then, make all of your current view models inherit this base type
public MyViewModel : BaseViewModel
{
public string SomeValue {get;set;}
}
Then in your controller, it is easy enough to add these values from the service you created from the first step
public ActionResult SomeAction()
{
var vm = new MyViewModel();
vm.Branch = LocalBranchService.GetLocalBranchValues(); //Local Branch Service has been injected with Ninject
//do other stuff
return View(vm);
}
However, that gets pretty tedious to add that to each controller action, so you can instead create a Result Filter to add it for you:
public class LocalBranchResultFilter : FilterAttribute, IResultFilter
{
public void OnResultExecuting(ResultExecutingContext filterContext)
{
//This method gets invoked before the ActionResult is executed.
filterContext.Controller.ViewData.Model.Branch = LocalBranchService.GetLocalBranchValues(); //Local Branch Service has been injected with Ninject
}
}
Now, you can just decorate your Controller and/or Actions with the filter (you could even set it in the Global Filters if you want).
You can embed the child actions into your layout or a view. You can even cache its output so you don't keep re-querying the database.
controller
[ChildActionOnly]
[OutputCache(Duration=500, VaryByParam="*")]
public ActionResult Info()
{
var localBranch = db.GetLocalBranch();
return PartialView("_Info", localBranch);
}
_Info view
This bit will get inserted into your other views
#model LocalBranch
<span>#Model.address</span>
<span>#Model.phone</span>
Use in _Layout or other view
<p>lorem ipsum...</p>
#Html.Action("Info")

Dotnetnuke Custom Module Settings

I have created a dotnetnuke module, it has multiple controls wrapped in a single moduleNow i want to access the settings variable across module, say for example i have a setting for dateformat, now the dateformat user selects should be used throughout moduleIt works fine with the view control which comes by default with Dotnetnuke (ChrisToc Template)But when i add new control it does not works, i also added proper inherits, it never throws compile error (in case it does not gets proper namespace)
Below is the code i am using:
public partial class ViewEntry : WireModuleBase
{
protected void Page_Load(object sender, EventArgs e)
{
Response.Write("SETTINGS: " + Settings["WireDateFormat"]);
}
}
Any help will be appreciated
I don't ever use the Setting dictionary in my module views. First, it leaves you open to code errors by having to hardcode the key string when accessing the setting. Second, it makes it hard to share with you business logic or other views. My preferred pattern for settings is to create an interface and class for my settings that provides class attribute for my settings and performs the plumbing calls to DNN core to get and set those settings.
Follow this link to a Codeplex project where you will find a class SettingsRepository and ISettingsRepository interface.
Once you modified the public properties for your settings (ie: WireDateFormat) into the class and interface, you can then use it in your module settings implementation.
Get the setting:
ISettingsRepository settingsCtrl = new SettingsRepository(this.ModuleId, this.TabModuleId);
txtSetting1.Text = settingsCtrl.Setting1;
Write the setting
ISettingsRepository settingsCtrl = new SettingsRepository(this.ModuleId, this.TabModuleId);
settingsCtrl.Setting1 = txtSetting1.Text;
Once the settings is stored (in this case using the TabModuleId, but you can use the ModuleID constructor overload if you want to share the setting across modules on a page), you can use the same "get" code in any of your module views or business logic.

Share session between two MVC applications

I have an older application that has an ActionResult function that I would like to include in another newer application.
I have found that I can add the older controller as a reference, Instantiate the controller as an object, and then call the ActionResult function accordingly, like so:
public ActionResult test()
{
OlderApplication.Controllers.PatronController temp = new OlderApplication.Controllers.PatronController();
return temp.Index();
}
My problem lies in the fact that inside temp.Index() it references the Session, and it is coming up as null instead of having a value that should exist. Can I make session available this way?
In the example you've given you don't have two MVC applications - you have a single application that shares the same codebase. An "application" is defined by the webserver, but I digress.
The reason the Session within temp is null is because you aren't initializing the Controller correctly - Controllers are not POCOs, they require initialization. Call temp.Initialize(), however you'll need to create the RequestContext instance yourself, like so:
RequestContext context = new RequestContext( this.Context, this.RouteData );
OlderApplication.Controllers.PatronController oldController = new OlderApplication.Controllers.PatronController();
oldController.Initialize( context );
return oldController.Index();
Although if you're going through this step, you might as well just wire up your old controller into your Area Registration and/or URI Routing tables, thus negating the need to write this code.
As Dai pointed out you're not creating the controller correctly.
I would suggest using the controller factory to create your controller.
var oldController = ControllerBuilder.Current.GetControllerFactory().CreateController(Request.RequestContext, "Patron");

Resources