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.
Related
I have some custom code which creates a structure definition based on some user inputs. The way it works is to set up a differential by getting structure definition requirements from user, generates the snapshot and finally I persist it into local storage on Fhir Server.
I tried the following code snippet to validate the StructureDefinition before persisting it to database, but the validationResult is always null no matter what the structureDefinition I will pass to it.
Could anyone let me know of the correct way to validate a customized StructureDefinition?
var structureDefinition = ...
ICollection<ValidationResult> validationResult = null;
DotNetAttributeValidation.TryValidate(structureDefinition, validationResult);
There's a third (optional) parameter to TryValidate that is called 'recurse', you should try setting that to "true", otherwise the validate will only do the direct elements of the structuredefinition, not the data inside the types etc.
I found nothing on the web what [Required] actually does. The msdn-article is not explorative at all.
static class Program
{
public static Main()
{
var vustomer = new CustomerClass();
}
}
public class CustomerClass
{
public string m_FirstName;
[Required]
public string m_LastName;
}
As far as i understand, that should throw an exception since m_LastName is required, but not set. But i don't get one. I don't get what it's good for and what this actually does.
RequiredAttribute, like all other attributes, does nothing by itself other than annotate something (in this case, a field of a type). It is entirely up to the application that consumes the type to detect the presence of the attribute and respond accordingly.
Your sample program does not do this, so the attribute does not have any visible effect. Certain frameworks such as ASP.NET MVC and WPF do check for and respond to the presence of the attribute.
This attribute is used by the Validator class to add validation errors based on any types that inherit from ValidationAttribute. This is used by MVC model validation, for example.
In C#, attributes are almost decoration to classes and properties.
Except for a few security related attributes, most do nothing. They are used by a higher-level framework to do something.
In the case of ASP.NET 4 MVC, only when the object is part of a request that attribute is used to generate an error.
If you want to use that attribute in any other environment, you must write code to inspect it.
It won't do anything from a plain old public static void Main()
Documentation on RequiredAttribute:
Specifies that a data field value is required.
However this validation is typically only performed in the UI layer. It is not "baked" into the constructor or other low-level usage. If you want to manually fire the validation you could do something like:
var customer = new CustomerClass();
var context = new ValidationContext(customer, serviceProvider: null, items: null);
var results = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(customer, context, results);
if (!isValid)
{
foreach (var validationResult in results)
{
Console.WriteLine(validationResult.ErrorMessage);
}
}
To add to the current answers, these are some of the practical uses I can think of out of my head:
Entity Framework use this to model the database as not nullable fields.
Javascript client side validation provides javascript libraries for checking if the input field has any data, else prevent the form submission avoiding unnecesary roundtrips to the server.
Server side validation (when doing model binding) also check when you are posting a model with that decorator that the attribute passed in is in the request. This determines if the model state should be set to an invalid state or not. (1)
There are also annotation for JSON.NET library that changes how the model is serialized/unserialized. I'm pretty confident (but not sure) that the Validate Schema of Json.net does take into consideration the 'Required' attribute when validating a schema.
Other attribute decorators are used on web services, but 'Required' is not one I know has it uses in this scenario.
Web api uses this attribute to mark the property as required on documentation help pages and model binding.
You can create your own application logic that can be aware of the 'Required' property. For example, on a view to mark the property with an * in the label if it's required.
This are some uses but you are not limited to them.
(1) Note: if your property is, for example, an int and you decorate it with Required, model state will never be on a invalid state. You should use Nullable properties for this use-cases.
This is a bit of a strange one with Sitecore... Basically I'm accessing an item from the Content API but it's not populating the Item.Fields hashtable with keys based on the text for the field (I guess I'd call this a field name) but rather with a GUID.
For example, here is some code I'm using to get an item:
var database = global::Sitecore.Configuration.Factory.GetDatabase("master");
var item = database.GetItem("/sitecore/content/Home");
item.Fields.ReadAll(); // edit, per recommendation... does not work
Sitecore.Data.Fields.Field f = item.Fields["SomeText"];
Assert.IsNotNull(f): // This fails
If I set a breakpoint and debug, I can see that there are values (indeed, the correct values) inside the Item.Fields hashtable, but the keys are all based on GUIDs rather than "field names" as most code samples regarding usage of this API suggest.
EDIT: Upon closer inspection, the DisplayName and Name fields are coming back as empty strings from the API (note these are clearly defined in Sitecore so still not sure what the issue is). It appears these might be used in conjunction with GUID as some sort of key for the hashtable.
Question: Is there something I'm doing wrong here? I've published the data template and the content item. Clearly the connection is being made because I'm getting results back from the API and even the correct values, just not the keys I'm expecting to use to reference the data values.
References:
http://sdn.sitecore.net/upload/sitecore6/content_api_cookbook-a4.pdf - checkout the example right at the top of page 28 where they access the "title" field. Also, check out the example directly below in 4.1.1 "How to Access System Fields" where they use static helpers with the GUIDs instantiated in a private static constructor. Is this the preferred method for accessing "user defined" fields?
Screenshot of sample data from Sitecore (notices the GUIDs as keys):
Code Samples from above linked document:
Accessing the "title" field:
Sitecore.Data.Database master = Sitecore.Configuration.Factory.GetDatabase("master");
Sitecore.Data.Items.Item home = master.GetItem("/sitecore/content/home");
Sitecore.Data.Fields.Field titleField = home.Fields["title"];
if(titleField!=null)
{
home.Editing.BeginEdit();
titleField.Value = "//TODO: replace with appropriate value";
home.Editing.EndEdit();
}
Accessing the system field "ArchiveDate":
Sitecore.Data.Database master = Sitecore.Configuration.Factory.GetDatabase("master");
Sitecore.Data.Items.Item sample = master.GetItem("/sitecore/content/home/sample");
Sitecore.Data.Fields.DateField archiveField =
sample.Fields[Sitecore.FieldIDs.ArchiveDate];
Decompiling the Sitecore.Kernel.dll we can see that:
public static class FieldIDs
{
// stripped down version
/// <summary>The ID of the 'Archive date' field.</summary>
public static ID ArchiveDate;
static FieldIDs()
{
FieldIDs.ArchiveDate = new ID("{56C15C6D-FD5A-40CA-BB37-64CEEC6A9BD5}");
}
}
If I understand correctly, you want the Fields collection to return all the fields available for that item, even if they do not have a value. By default, Sitecore will only return those fields that have a value.
You can solve this by calling the ReadAll() method before accessing the fields collection.
So in your example:
item.Fields.ReadAll();
Sitecore.Data.Fields.Field f = item.Fields["SomeText"];
Assert.IsNotNull(f): // This succeeds
I had a problem with identical symptoms. The root cause for me was a publishing issue. The folder containing my template was not published, though the template itself was. So I could see the fields in the debugger with the correct values and ids, but not the names. The solution was to ensure that all the parents of my template were also published.
So, I ended up going the route I mentioned in the question (which is what Sitecore uses internally and, #technophoria414 mentioned, a Sitecore developer best practice).
Basically:
namespace MyProject.Core.Data.Sitecore.Fields
{
public static class ContentItem
{
// stripped down version
public static ID DESCRIPTION_TEXT;
static ContentItem()
{
DESCRIPTION_TEXT= new ID("{56C15C6D-FD5A-40CA-BB37-64CEEC6A9BD5}"); // this will be some GUID out of Sitecore
}
}
}
Usage would be something like this:
var query = string.Format("fast:/sitecore/content/HomePageItems//*[#ContentSlug='{0}']", input);
var item = Database.SelectSingleItem(query);
var descriptionText = item.Fields[ContentItem.DESCRIPTION_TEXT].Value;
I've a c# application(in fact it runs into a windows service or like a windows application).
In receive the configuration for one run of the service/application from an XML file.
This XML file is in fact only my data serialized/deserialized in XML. It's one of my other application that generate it.
Before running the business code, I would like to ensure that the configuration file is valid.
By this I mean things like "This String must not be null", "This TimeSpan must have a value greater than XYZ", ...
So only verification that can be made by seing the content of the field(no need to access to something else).
It reminds me a lot the Data Annotation that I used in asp.Net MVC, and I would like if there is something similar for simple c# code, without having to load the whole asp.net MVC dll.
My other option is to implement a method "Validate()" which throw an exception if one field is incorrect, but I will have a lot of if(String.IsNullOrEmpty() and other dummy validations.
I do not want to implement myself a big validator that uses reflexion, it's a bit overkill for only a small configuration file verification.
The application which generate those file could also be interessted to use those same validation.
Edit: I've to use .Net 3.5
This question looks like a duplicate of the following SO question.
Using ASP.Net MVC Data Annotation outside of MVC
Edit: Seeing as you say the ValidationContext isn't available I would recommend writing some custom code that uses Reflection and evaluates all the attributes on the properties for you.
See the answer to this question for an example of how it can be done.
ASP.Net MVC 2 Controller's TryValidate doesn't validate the List<> items within the model
I used the IDataErrorInfo(the same class will be used in the wpf application). And with a custom method where I check every possible attribute here.
Here is the method:
public Boolean IsModelValid()
{
Boolean isValid = true;
PropertyInfo[] properties = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo p in properties)
{
if (!p.CanWrite || !p.CanRead)
{
continue;
}
if (this[p.Name] != null)
{
isValid = false;
}
}
return isValid;
}
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).