I would like to use my own localization implementation instead of the default resource file system the framework provides. I need also to configure the validators in a configuration file.
My problem is that I don't know how to access the Validator objects to change their message templates after I have instantiated the ConfigurationValidatorFactory. I can create a validator for my object like this:
var cfgSrc = new FileConfigurationSource("Validations.xml");
var factory = ConfigurationValidatorFactory.FromConfigurationSource(cfgSrc);
Validator val = factory.CreateValidator<MyObj>();
, but 'val' above is then of type GenericValidatorWrapper and has no properties to access the 'true validator' instances.
After validation I can see the true Validator instances, but then it is too late to change their template text. The final message (containing the limit values) has then already been created and changing the template won't re-create the message.
Any suggestions?
Enterprise Library version is 5.
You might be able to use reflection to modify the validator properties that you are interested in. But that is really messy and Kludgy.
What if you simply replaced the message text on the validation results after validation?
The message in the configuration file could be a place holder (e.g. NAME_INVALID_LENGTH) and you could replace that with your actual text.
var cfgSrc = new FileConfigurationSource("Validations.xml");
var factory = ConfigurationValidatorFactory.FromConfigurationSource(cfgSrc);
Validator val = factory.CreateValidator<MyObj>();
MyObj myObj = new MyObj();
myObj.Name = "Swiper McFoxy";
ValidationResults results = new ValidationResults();
foreach (ValidationResult result in val.Validate(myObj))
{
results.AddResult(
new ValidationResult(
GetMessage<MyObj>(result.Message, new CultureInfo("en")),
result.Target,
result.Key,
result.Tag,
result.Validator,
result.NestedValidationResults));
}
Update
OK, if you need the template text you can investigate poking around via reflection but that still looks really messy.
Given your requirements (configuration file, maintain templates) what I would probably do would be to create multiple configuration files each with the template text you want.
That's a bit of a maintenance nightmare so I would refine that idea to:
Create my configuration file Validations.xml and instead of putting my templated text in I would use a unique placeholder for each message (e.g. NAME_INVALID_LENGTH but even more unique-ish).
Then I would write a small piece of code/tool that would, for every locale you are supporting, replace the unique placeholder with the locale specific templated text. And copy the resultant file to a locale specific folder under the OutDir (e.g. bin\debug\en-US\Validations.xml). This is similar to how .NET deploys satellite assemblies so it is not that far a stretch.
Run the tool above as a post-build step. The tool can flag placeholders that it can't find so that it gets reported during the build. This should help keep the placeholders and the template text in sync.
Modify your code to use the locale specific configuration file:
CultureInfo culture = new CultureInfo("en-us");
var cfgSrc = new FileConfigurationSource(culture.Name + #"\Validations.xml");
var factory = ConfigurationValidatorFactory.FromConfigurationSource(cfgSrc);
Validator val = factory.CreateValidator<MyObj>();
You can abstract the culture specific code behind a helper method so that it's easier to read and maintain. e.g. GetValidationConfigurationSource(culture).
Related
I am trying to localize a hosted service in response to a runtime condition which is fed in a variable lang, which represents a 2-letter ISO code (such as 'en', 'es', ...).
I set the localization service in my Startup.cs like this:
services.AddLocalization(options => { options.ResourcesPath = "xresx"; });
In my controller I have the following code:
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(lang);
I know this works, because when I pass in lang='es' the following:
var check = Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName;
returns the correct value check = 'es'.
But then the next statement:
var msg = Resources.TestMsg
picks up my the value from my English resource file Resource.resx instead of Resource.es.resx.
What am I doing wrong, and how can I make it work?
Thanks!
OK, so what ultimately worked for me was following exactly the steps in this guide: https://joonasw.net/view/aspnet-core-localization-deep-dive
This link is the only source I've found that worked for me, and it was better than Microsoft's own documentation (which omits potential pitfalls like not naming your Resource files a very certain way).
Let me summarize some points:
One needs to inject IStringLocalizer into their controller, eg:
IStringLocalizer<MyController> _localizer;
Then inside the controller you can localize your strings, eg:
_localizer["STRINGKEY"]
where STRINGKEY is a key from your resource file.
Make sure to name your resource files properly. This is very important and is not documented by Microsoft as far as I know! Cost me a lot of time until I stumbled over the web link I've referenced above.
I was naming my files like this:
Resource.resx, Resource.es.resx etc
and the localized wasn't finding the values, instead just returning the key itself.
Eg, _localizer["STRINGKEY]" would return "STRINGKEY" rather than the corresponding value in the resource file.
So you must name your files instead using your Controller's name, like this:
Controllers.MyController.resx, Controllers.MyController.es.resx
These are the main points to remember. Sadly, Microsoft documentation glosses over a lot of this stuff.
This is a .NET 4.5 project, it has been set up long time before my time here, so not a lot of details as to why it's been done this way.
We have a web application where each language has its own resx file.
On every page load, the values are loaded from the resx file, the usual way.
string strLangResourceValue = (string)HttpContext.GetGlobalResourceObject(lang, strLangResourceKey);
This works fine. Might be worth mentioning that this line of code is in a separate project so it can be shared with multiple other projects we have under the solution.
In the views (Razor) we do #Application.GetLangResource("Some_Key"); to retrieve the value we need. The language is taken from the session model stored in HttpContext.
The issue raises when we change the language, every partial view on that page is translated accordingly except for that 'user settings' page we changed the language on. What makes it strange is that we have two views, one read only page to only display the data and one which contains the actual form to modify the values and neither one of them is translated.
The known issues are:
I can't now set CultureInfo within the web application, I need to use the session model to get this information and use the above line of code to grab the data.
Force refreshing the browser, clearing cookies and cache does not fix the issue.
Only restarting the IIS server fixes the issue (the resx file is recompiled)
I know that the resx file are compiled during runtime and are static. They are not changed in the code during application run, it's only the user session that changes. The above list has been attempted to get to the bottom of the issue, but restarting the application every time someone changes their language is not an option (as you might of guessed).
Is there an obvious solution to this that I'm missing? There is no resx error, it's IIS cache (or at least it seems like it is) on just the two specific pages. They are built the same way as all the other ones, just not switching the language. This applies to all users when they change their languages.
I have tried to manually clear the cache using the below lines of code, that did not do the job issue still persisted.
Resources.AppSettings.ResourceManager.ReleaseAllResources();
Resources.English.ResourceManager.ReleaseAllResources();
Resources.Dutch.ResourceManager.ReleaseAllResources();
HttpResponse.RemoveOutputCacheItem("/Views/User/userViewDetails.cshtml");
foreach(System.Collections.DictionaryEntry entry in HttpContext.Cache) {
HttpContext.Cache.Remove((string) entry.Key);
}
I finally got to the bottom of my own problem, but I still don't understand how that works. So, the problem was that on first view render it would grab the values from resx for the corresponding language but not on any subsequent renders (just as if it would cache those values to cut access times to resx, which is great).
I ended up implementing my own DataAnnotationsModelMetadataProvider and it did the job great.
So for anyone interested at my solution it looks something along the lines of:
public class LocalizedDataAnnotationsModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
var meta = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName); // Get metadata of the property
string resXKey = string.Empty;
object attribute = attributes.OfType<DisplayLabel>().FirstOrDefault(); // Try to get the DisplayLabel annotation
// If DisplayLabel is found then...
if (attribute != null)
{
resXKey = ((DisplayLabel)attribute).Key; // Grab the resx key added to the annotation
meta.DisplayName = Application.GetLangResource(resXKey) + meta.DisplayName; // Retrieve the language resource for the key and add back any trailing items returned by the annotation
}
// DisplayLabel is not found...
if (attribute == null)
{
attribute = attributes.OfType<DisplayNameLocalizedAttribute>().FirstOrDefault(); // Try to get the DisplayNameLocalizedAttribute annotation
// If annotation exists then...
if (attribute != null)
{
resXKey = ((DisplayNameLocalizedAttribute)attribute).Key; // Grab the resx key added to the annotation
string resXValue = Application.GetLangResource(resXKey); // Retrieve the language resource for the key
string finalValue = resXValue; // Create a new string
if (((DisplayNameLocalizedAttribute)attribute).IsLabel) // Check if property annotation is set to label
{
finalValue = resXValue + meta.DisplayName; // Add back any trailing items returned by the annotation
}
meta.DisplayName = finalValue; // Set the DisplayName of the property back onto the meta
}
}
return meta;
}
}
The DisplayLabel and DisplayNameLocalizedAttribute are custom property annotations, which store the resx keys and any additional data such as (if it's a label so typically we can add ":") at the end if it's for an input.
The Application.GetLangResource is a function which grabs values from resx files, based on given language and if the values is not found it would return a suitable message.
To make the server use the created DataAnnotationModelMetadataProvider you need to set it as the Current one on Application_Start event inside Global.asax.
To do that, you would do:
LocalizedDataAnnotationsModelMetadataProvider loca = new LocalizedDataAnnotationsModelMetadataProvider();
ModelMetadataProviders.Current = loca;
It is important (if you get errors in global.asax) that you're using System.Mvc and not the other import that appears there for ModelMetadataProviders as it won't let you assign the new Provider.
Is there a way to expose Razor syntax and (custom) helpers to people , but say ... not allow them to create code blocks or , to only limit them in the usage of the helpers and to not give them the power to execute pure C# code in the views ?
Any ideas and pointers to similar solutions are welcome !
update:// I would like to give the users the power to write their own HTML and access only to a list of html helpers. Mostly the default ones and the ones i create.
For example i do not want them to be able to execute code within #{ //code } blocks and
Also no using and #model ( not sure about this one)
only have access to #Html.* #if else for foreach
or better yet , give them access only to specific namespaces (this just a thought tho)
update://
After some testing , i found out that RazorEngine does as close as to what i'm trying to do : run the views in isolated environment and add access to specific namespaces.
I would not recommend you doing that. There simply is not an easy and reliable way to give them this ability without compromising the security of your site. If you trust your users then you could do it. If you don't then a templating engine such as DotLiquid is something far more appropriate for this purpose.
There is a project called RazorEngine, built upon Microsoft's Razor, that allows you to parse that syntax without being in the context of returning an MVC view. Here's how it's used:
string template = "Hello #Model.Name! Welcome to Razor!";
string result = Razor.Parse(template, new { Name = "World" });
You can also specify a customized template base, which should allow you to define only the Html Helpers you want to expose to your users:
Razor.SetTemplateBase(typeof(HtmlTemplateBase<>));
string template =
#"<html>
<head>
<title>Hello #Model.Name</title>
</head>
<body>
Email: #Html.TextBoxFor(m => m.Email)
</body>
</html>";
var model = new PageModel { Name = "World", Email = "someone#somewhere.com" };
string result = Razor.Parse(template, model);
you may try to change razor view engine and related classes to check for disallowed situations.
When source is generated (view engine generates a source file to compile ), you have to check it manually (by parsing c# or vb.net code). It is possible, but not feasible (really).
Even if you have managed to parse and check code, you have to identify your code (which is allowed) and customer code (which has restrictions).
At the end you have to accept the fact you can not really disallow anything other than using another template engine.
because
Your customers will find a way to make their views look like yours.
You cannot limit most basic required features like var r = new Random();
You cannot estimate what most basic requirements are
you cannot say No to your customers when they need to use their custom libraries
By the way, you may try another thing. Write a virtual path provider, and convert customer templates written in AviatrixTemplate when requested by runtime. By using this route, you still use razor engine, loose only a slight time when converting (it is one time only). But your AviatrixTemplate won't be hilighted, and you still need to check for disallowed code.
PS: a basic loop may give your users more then you want. for example following code allows creation of a class and call it for one time. they may use fully qualified class name or may use Activator.CreateInstance.
#for (var r = new Random(); r != null; r = null)
{
#r.NextDouble()
}
just do not bother.
I have never done this before, but it sounds like you want to give users the ability to write code and have it compiled for use, yes?
If so, you may want to look into the CSharpCodeProvider class, the RazorTemplateEngine class and the System.CodeCom.Compiler namespace.
Have a look here for some information on those classes:
CSharpCodeProvider: http://support.microsoft.com/kb/304655
RazorTemplateEngine: http://msdn.microsoft.com/en-us/library/system.web.razor.razortemplateengine(v=vs.111).aspx
I have an Windows Forms application VS 2008 - C#, that uses app.config.
In execution time, in Menu option of my application, I want editing values of app.config, save it and restart application.
any sample source code, any good patterns and practices ??
edit:
in MSDN Forums, Jean Paul VA:
Create an test windows forms application and add an app.config into it.
Add reference to System.confguration
Add a key named "font" in appSettings with value "Verdana"
Place a button on form and on click of it add the modification code.
System.Configuration.Configuration configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
configuration.AppSettings.Settings.Remove("font");
configuration.AppSettings.Settings.Add("font", "Calibri");
configuration.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("appSettings");
what you think about it ?
I don't think you can actually write to the configuration file at runtime - it may well be read-only, however, there may be a way around this by re-writing the file (with proper alterations as required) and essentially replacing the existing one, then, further, restarting the application to load the new values (I highly doubt this is desirable and personally would not try to instrument this malarkey).
You may also just consider storing application settings in the settings file which are easily manipulated in this way.
To use settings, first let's assume you have a Settings.settings file (if not then create one: Add Item->Settings File), then we have a setting configured named MyUnicornsName; In order to make a change and persist it you can simply do as follows:
Settings.Default.MyUnicornsName = "Lucifers Spawn";
Settings.Default.Save();
Similarly, to read a setting:
ATextDisplayControl.Text = Settings.Default.MyUnicornsName
In case you don't know, Visual Studio will open the settings editor when you open/double click the settings file in the IDE; using this interface you can add and edit your initial settings and their values, and string is not the only supported value, all primitives can be used, and, as far as I know, any serializable value too.
Is there any reason you can't use the usual auto-gen'd Properties.Settings to store the changing data in a settings file instead? One great thing is that you know what you're changing so you don't even have to restart the application!
Using Settings in C#
Runtime access of settings is as easy as:
this.BackColor = Properties.Settings.Default.myColor;
(There is no good pattern for modifing app.config itself simply b/c it's designed to be readonly in that context, with expert-user settings.)
Use the Project->Properties->Settings for these kinds of things.
well actually the Properties in the App.config are ReadOnly so u cant do it.
But there's a trick............................
In the Settings.cs file create a Function or method that is public so that it can be available with Properties.settings
and write the following code..
public void ChangeProperty(string propertyname, string value)
{
this[propertyname] = value;
}
remember to pass the exact string of the property name to the method. or better create a Writeonly Property for the setting.
update
here is a code for setting as a property, i am taking a connection string as an example, bet it can be anything. remember the property stored is of Object type so you can create property specific to that..
public string MyCustomConnectionstring
{
set
{
//replace the string with your connection string otr what ever setting you want to change
this["myConnectionString"] = value;
}
}
Now you can easily use this Property to change the ConnectionString at run time...
I have seen the following code:
[DefaultValue(100)]
[Description("Some descriptive field here")]
public int MyProperty{...}
The functionality from the above snippit seems clear enough, I have no idea as to how I can use it to do useful things. Im not even sure as to what name to give it!
Does anyone know where I can find more information/a tutorial on these property attributes?
I would be also interested in any novel / useful tasks this feature can do.
The functionality from the above
snippit seems clear enough,
Maybe not, as many people think that [DefaultValue()] sets the value of the property. Actually, all it does to tell some visual designer (e.g. Visual Studio), what the code is going to set the default value to. That way it knows to bold the value in the Property Window if it's set to something else.
People have already covered the UI aspect - attributes have other uses, though... for example, they are used extensively in most serialization frameworks.
Some attributes are given special treatment by the compiler - for example, [PrincipalPermission(...)] adds declarative security to a method, allowing you to (automatically) check that the user has suitable access.
To add your own special handling, you can use PostSharp; there are many great examples of using PostSharp to do AOP things, like logging - or just code simplification, such as with automatic INotifyPropertyChanged implementation.
They are called Attributes, there is a lot of information in msdn, e.g. http://msdn.microsoft.com/en-us/library/z0w1kczw.aspx
In general they don't "do" anything on their own, they are used by some other code that will use your class. XmlSerialization is a good example: XmlSerializer (provided by Microsoft as part of the framework) can almost any class (there are a number of requirements on the class though) - it uses reflection to see what data is contained in the class. You can use attributes (defined together with XmlSerializer) to change the way XmlSerializer will serialize your class (e.g. tell it to save the data as attribute instead of an element).
The ones in your example is used by the visual designer (i.e. MS Expression Blend and Visual Studio designer) to give hints in the designer UI.
Note that they are metadata and will not affect the property logic. Setting DefaultValue for instance will not set the property to that value by default, you have to do that manually.
If you for some reason want to access these attributes, you would have to use reflection.
See MSDN for more information about designer attributes.
We use it to define which graphical designer should be loaded to configure
an instance of a specific type.
That is to say, we have a kind of workflow designer which loads all possible command
types from an assembly. These command types have properties that need to be configured,
so every command type has the need for a different designer (usercontrol).
For example, consider the following command type (called a composite in our solution)
[CompositeMetaData("Delay","Sets the delay between commands",1)]
[CompositeDesigner(typeof(DelayCompositeDesigner))]
public class DelayComposite : CompositeBase
{
// code here
}
This is information is used in two places
1) When the designer creates a list of commands, it uses the CompositeMetaData
to display more information about the command.
2) When the user adds a command to the designer and the designer creates
an instance of that class, it looks at the CompositeDesigner property,
creates a new instance of the specified type (usercontrol) and adds it
to the visual designer.
Consider the following code, we use to load the commands into our "toolbar":
foreach (Type t in assembly.GetExportedTypes())
{
Console.WriteLine(t.Name);
if (t.Name.EndsWith("Composite"))
{
var attributes = t.GetCustomAttributes(false);
ToolboxListItem item = new ToolboxListItem();
CompositeMetaDataAttribute meta = (CompositeMetaDataAttribute)attributes
.Where(a => a.GetType() == typeof(Vialis.LightLink.Attributes.CompositeMetaDataAttribute)).First();
item.Name = meta.DisplayName;
item.Description = meta.Description;
item.Length = meta.Length;
item.CompositType = t;
this.lstCommands.Items.Add(item);
}
}
As you can see, for every type in the assembly of which the name ends with "Composite",
we get the custom attributes and use that information to populate our ToolboxListItem instance.
As for loading the designer, the attribute is retreived like this:
var designerAttribute = (CompositeDesignerAttribute)item.CompositType.GetCustomAttributes(false)
.Where(a => a.GetType() == typeof(CompositeDesignerAttribute)).FirstOrDefault();
This is just one example of how you might be able to use custom attributes,
I hope this gives you a place to start.
These attributes customize the design time experience.
http://msdn.microsoft.com/en-us/library/a19191fh.aspx