I'm using ef6 with mvc5.
My project need multiple language. So I need create a DataAnnotations Attribute for validation these field.
For example: I have a Id property:
public int Id { get; set; }
For validation I need add annotations like
[Display("User Id")]
Required(ErrorMessage = "Please input the Id")
public int Id { get; set; }
But I need use multiple language , So I create a new DataAnnotations Attribute(https://stackoverflow.com/a/2432520/1900498):
public class LocalizedDisplayAttribute : DisplayNameAttribute
{
public LocalizedDisplayAttribute(string resourceId)
: base(GetMessageFromResource(resourceId))
{ }
private static string GetMessageFromResource(string resourceId)
{
// TODO: Return the string from the resource file
}
}
It works fine , but it will cache the result, then when session changed(I'm use session save the user website page language mode. like 1 mean English, 0 mean other language), it still not change, this is a problem for me.
second problem is: I don't know how to rewrite RequiredAttribute for let user know , some field can't empty.
but I also find there is another problem , looks like I need to rewrite the message about numeric field......(xx field must be numeric)
So Is there any way can rewrite the validation rule, let me decide the error message for Required, Range, Numeric...... and it will cache it but when session changed, it will read again?
For example:
// if session changed rewrite rule message for current language
if (session["language"].ToString() != LastLanguage)
{
if (session["language"].ToString() == "1")
//English
{
RequiredMessage = "the field {0} must be required";
NumericMessage = "the field {0} must be a number";
LastLanguage = 1;
} else{
// other language
RequiredMessage = "xx xx {0} xxxxxxxxx";
NumericMessage = "xx xx {0} xxxxxxxxxx";
LastLanguage = 0;
}
}
Of course, not only the validation message, I need globalization the field display name too.
DataAnnotation already provides globalization support:
[Display(ResourceType = typeof(Resource), Name = "Test")]
[Required(ErrorMessageResourceType = typeof(Resource), ErrorMessageResourceName = "TestRequired")]
public string Test { get; set; }
To change the current culture in the global.asax
private void Application_AcquireRequestState(object sender, EventArgs e)
{
if (Context != null && Context.Session != null)
{
string language = Context.Session["language"] as string;
if (language != null)
{
Thread.CurrentThread.CurrentCulture = new CultureInfo(language);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(language);
}
}
}
Related
I want to pass to external web service some values of an entity (Case/incident) while new record is going to be created.
I have a model for preparing data which have to be sent to web service as below:
public class TicketViewModel
{
public string CaseID { get; set; }
public string Subject { get; set; }
public string Description { get; set; }
public string CreateTime { get; set; }
public string Owner { get; set; }
public string States { get; set; }
public string Assigned { get; set; }
}
Here is my code inside Execute() method:
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = factory.CreateOrganizationService(context.UserId);
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
try
{
var entity = (Entity)context.InputParameters["Target"];
if (entity.LogicalName != "incident") // The logical name for Case entity
return;
Guid recordID = entity.Id;
var ticket = new CaseViewModel
{
// Retrieving Intended Fields Value
};
BasicHttpBinding httpBinding = new BasicHttpBinding();
httpBinding.Name = "HttpBinding_Service";
httpBinding.Security.Mode = BasicHttpSecurityMode.None;
httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
httpBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
httpBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
EndpointAddress epa = new EndpointAddress(#"webservice/url/address");
CallChamberPortalSoapClient tcClient = new CallChamberPortalSoapClient(httpBinding, epa);
var res = tcClient.addTicket(//Passing Intended Fields Value);
entity["X"] = res.ToString();
}
catch (Exception ex)
{
throw new InvalidPluginExecutionException("Failed to register ticket by this error: " + ex.Message);
}
My first question is how to retrieve intended fields value on Create new record? I have used entity["X"] to get value of "X" field but nothing returned.
My second question is how to set value of a field on Update a record? Using same expression ( entity["X"] = "NewValue" ) not worked for me.
Note: sample static data has sent to web service successfully and it returned true as result.
EDIT:
I tried to get values as below but have error in CRM create record event.
ColumnSet cs = new ColumnSet(new string[] {
"ticketnumber", "title", "description", "createdon", "customerid", "new_peygiriii", "createdby" });
Entity wholeCase = service.Retrieve("incident", recordID, cs);
Owner = wholeCase.GetAttributeValue<EntityReference>("customerid").ToString();
Error:
Unable to cast object of type Microsoft.Xrm.Sdk.OptionSetValue to type
Microsoft.Xrm.Sdk.EntityReference
Thanks.
First, You should register your plugin in Dynamics as Post operation (create). Reason once the record is created in System, you will get it's Guid and so on. This is best way and in addition make your plugin Asynchronous (syn only if it is a real must for your use case).
Now when you create a recrod in crm plugin will get it's context as you are doing.
var entity = (Entity)context.InputParameters["Target"];
now you can get particualr fileds value, you do something like below
if(entity.contains("field name")){
var recordName=entity.GetAttributeValue<string>("field name");
}
if you want optionset values you do something like below
if(entity.contains("optionset field name")){
int selectedTopic = entity.GetAttributeValue<OptionSetValue>("optionset field name").Value
String text = entity.FormattedValues["optionset field name"].ToString();
}
To set up? what type of data you want to set up, assuming you want to set up optionset value
entity["X"] = new OptionSetValue(INDEX)
The INDEX is an int you can look up in your optionset editor (default values are several digit long).
I hope this wasn't asked before, I couldn't find an easy solution in MSDN or here.
The windows phone 8.1 application is deployed in more than one language.
To do so I use the default language (english) in Strings\en-US\Ressources.resw and installed the Multilingual App Toolkit with all further languages added there.
To change the language, I have the following code:
private void changeLang(string cul)
{
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = cul;
Windows.ApplicationModel.Resources.Core.ResourceContext.GetForViewIndependentUse().Reset();
Windows.ApplicationModel.Resources.Core.ResourceContext.GetForCurrentView().Reset();
if (Frame != null)
Frame.Navigate(typeof(MainPage));
}
which can be called by
changeLang("en-US");
After that I have to restart the application (couldn't make it work without restart yet).
The problem is my implementation. I created a page called Settings where I want to provide the user the possibility to change the language.
Now I want to provide the user a ComboBox with all the languages I have translated. By default the selected ComboBoxItem should show the current language of the application (not the Systems language, as the user might already have had changed the language).
Here my solution to the problem, I hope this might be useful to others as well.
First we create a new struct:
public class ComboboxItem
{
public string Text { get; set; }
public object Value { get; set; }
public override string ToString()
{
return Text;
}
}
Then on the OnNavigate part on the Form we add the following code:
settings_language_cb.Items.Add(new ComboboxItem { Text = "Deutsch", Value = "de-DE" });
settings_language_cb.Items.Add(new ComboboxItem { Text = "English", Value = "en-US" });
var curLangItem = settings_language_cb.Items.SingleOrDefault(x => (x as ComboboxItem).Value.ToString() == CultureInfo.CurrentCulture.Name);
settings_language_cb.SelectedItem = curLangItem;
settings_language_cb.PlaceholderText = (curLangItem as ComboboxItem).Text;
And that's all.
You can try something like this
class LanguageCode
{
string Name { get; set; },
string CodeName { get; set; }
}
var langs = new List<LanguageCode>();
langs.Add(new LanguageCode() { Name = "English", CodeName = "en-US" });
langs.Add(new LanguageCode() { Name = "Deutsch", CodeName = "de-DE" });
// ... and so on ...
settings_language_cb.Items.Add(langs);
settings_language_cb.SelectedIndex = 0;
On the ComboBox, change the code to:
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var si = settings_language_cb.SelectedItem as LanguageCode;
if(si != null)
changeLang(si.CodeName); // changeLang("de-DE");
}
#MrEko
it's easy to get the selected item.
First you have to create a SelectionChanged event in your XAML Combobox and then you will get the selected item as following:
(myXAMLComboBox.SelectedItem as ComboboxItem).Value.ToString();
and here the whole thing in action. (note that oldLang is a constant that I save when I change the language and changeLang is the function that changes the language). And of cause, after changing the language, you have to restart your App, so it takes effect.
private void Page_Settings_LanguageComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (oldLang != (PageSettings_Language_cb.SelectedItem as ComboboxItem).Value.ToString())
{
try
{
changeLang((PageSettings_Language_cb.SelectedItem as ComboboxItem).Value.ToString());
ShowRestartMessageBox();
}
catch (Exception)
{ }
}
}
I am trying to create a program that mimics an ATM. In my program, I need to check if the string that a user enters matches the Name property of any objects within a list of objects. If it does not match, then the account is automatically added with some other default values. If it does match, then I need to set the variables that are accessed on another form to the properties of that account object. Additionally, those properties will need to be updated from the other form, so that the object is kept current. I think that I can figure out how to update those properties, but I am having difficulty with trying to set the variables to the current account, more specifically, how to access the properties of the matching account. My class constructor is as follows:
class Account
{
private string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
private int acctNum = 0;
public int AcctNumber
{
get
{
return acctNum;
}
set
{
acctNum = value;
}
}
//initialize the CheckBalance value to 100.00
private decimal checkBalance = 100.00M;
public decimal CheckBalance
{
get
{
return checkBalance;
}
set
{
checkBalance = value;
}
}
public Account(string Name)
{
this.Name = Name;
}
private decimal saveBalance = 100.00M;
public decimal SaveBalance
{
get
{
return saveBalance;
}
set
{
saveBalance = value;
}
}
}
This works out just fine, as the only constructor that I need is the Name property, while the other properties are automatically set to a base value. The list and relevant code that I currently have are as follows:
//variable that will be used to check textbox1.Text
string stringToCheck;
//array of class Account
List<Account> accounts= new List<Account>();
public MainMenu()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//set value to user's input
stringToCheck = textBox1.Text;
//set a var that only returns a value if the .Name already exists
var matches = accounts.Where(p => p.Name == stringToCheck);
//check through each element of the array
if (!accounts.Any())
{
accounts.Add(new Account(stringToCheck));
}
else if (matches != null)
//set variables in another form. not sure if these are working
Variables1.selectedAccount = ;
//is this calling the CheckBalance of the instance?
Variables1.selectedCheckBalance = accounts[i].CheckBalance;
//same thing?
Variables1.selectedSaveBalance = accounts[i].SaveBalance;
//switch to form
AccountMenu acctMenu = new AccountMenu();
this.Hide();
acctMenu.Show();
}
In the above code, the "else if (matches != null)" is more of a filler, since I am not sure what to use. Of course, I also need to re-write the portion "if (!accounts.Any())" because once the list is populated with at least one object, this code will never occur again. So, really, I just need to know how to check for a matching account and how to access the properties of that account so that I can set the Variables1 properties to match. Thanks for any help!
If it works for your particular situation, var account = accounts.FirstOrDefault(p => p.Name == stringToCheck) will give you the first account in the collection that matches the expression or null if nothing exists.
check if account != null to ensure you do not get a null reference exception when trying to get property values.
Then, use account.CheckBalance to get the property value for that particular account.
I may not be fully understanding the question and cannot comment because I do not have a 50 reputation : (
I am using MVC5, and I have a ViewModel for my View, which contains a simple form with the following fields:
MinFirstNameLength
FirstName
MinLastNameLength
LastName
Now, I wish to apply a validation rule on FirstName, based on the value of MinFirstNameLength, and similarly for LastName using MinLastNameLength. I want to do this on the client-side too.
So, I used MVC's unobtrusive client side validation feature. I made a custom validation attribute, implementing the IClientValidatable interface. The GetClientValidationRules method looks like this:
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
string ErrorMessage = ErrorMessageString;
ModelClientValidationRule NameMinLengthRule = new ModelClientValidationRule();
NameMinLengthRule.ErrorMessage = ErrorMessage;
NameMinLengthRule.ValidationType = "nameminlength";
NameMinLengthRule.ValidationParameters.Add("minlengthpropname", MinLengthPropName);
yield return NameMinLengthRule;
}
This validation attribute is applied on the FirstName and LastName properties like this:
[NameMinLength("FirstNameMinLength",ErrorMessage = "First Name must be at least {0} characters"]
public string FirstName { get; set; }
[NameMinLength("LastNameMinLength",ErrorMessage = "Last Name must be at least {0} characters"]
public string LastName { get; set; }
Also, I have the client side validation functions in a .js file elsewhere, which looks like this:
$.validator.addMethod("nameminlength",
function (
value,
element,
params
) {
return value.length >= parseInt($(params).val());
});
$.validator.unobtrusive.adapters.add("nameminlength", ["minlengthpropname"], function (options) {
var paramField = "#" + options.params.minlengthpropname;
options.rules["nameminlength"] = paramField;
var errormessage = options.message;
options.messages["nameminlength"] = errormessage;
});
My question is, how do I assign the value in the textbox for MinFirstNameLength to the placeholder {0} in my error message for FirstName, so that the error message reads First Name must be at least [value] characters?
I tried adding modifying my $.validator.unobtrusive.adapters.add method as follows:
$.validator.unobtrusive.adapters.add("nameminlength", ["minlengthpropname"], function (options) {
var paramField = "#" + options.params.minlengthpropname;
options.rules["nameminlength"] = paramField;
var errormessage = options.message;
options.messages["nameminlength"] = errormessage;
$(paramField).blur(function () {
// change error message in the 'data-val-nameminlength' attribute of the HTML element on which validation is applied
var newErrorMessage = options.messages["nameminlength"].replace("{0}", $(paramField).val());
$(options.element).attr("data-val-nameminlength", newErrorMessage);
// change error message inside the error message span generated for displaying this error
$(options.element).siblings(".field-validation-valid").html(newErrorMessage);
});
});
But both these tricks didn't work. The HTML markup did change to modify the error appropriately, but this change wasn't reflected in the page.
Currently, the error message I see is:
First Name must be at least #FirstNameMinLength characters
The placeholder is replaced automatically by the id of the control which is used as a parameter in the validation rule.
Where does this error message come from? How do I modify it at runtime?
I think you should just format your message on server side in the IsValid method of your NameMinLength validation attribute. Here's a small example on how to do it.
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var conditionalPropertyInfo = validationContext.ObjectType.GetProperty(this.MinLengthPropName);
var conditionalPropertyValue = conditionalPropertyInfo.GetValue(validationContext.ObjectInstance, null);
this.ErrorMessage = string.Format(this.ErrorMessage, conditionalPropertyValue.ToString())
// YOUR OTHER CODE HERE
}
This should replace the placeholder with the correct value from MinLengthPropName property. In this case the error message will be formatted before moving it to client side. So no additional logic required for this.
EDIT:
Hm, just thought that you might want to validate based on user input which is really wierd. Actually the fact that you might have different min length restrictions for the same field doesn't make sense to me but as long as it' not based on user input it is much more secure.
UPDATED WITH CLIENT SIDE SOLUTION:
I did a simple test with a similar attribute and it works for me
Edit Model:
[MinCustomLength("MinLenthDestURI", "Dest URI must be at least {0} characters")]
public string DestinationURI { get; set; }
public int MinLenthDestURI { get; set; }
Attribute code:
public class MinCustomLengthAttribute : ValidationAttribute, IClientValidatable
{
private String PropertyName { get; set; }
public MinCustomLengthAttribute(String propertyName, String errormessage)
{
this.PropertyName = propertyName;
this.ErrorMessage = errormessage;
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
// Just for test server side validation always returns Success
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var modelClientValidationRule = new ModelClientValidationRule
{
ValidationType = "mincustomlength",
ErrorMessage = this.ErrorMessage
};
modelClientValidationRule.ValidationParameters.Add("prop", PropertyName);
yield return modelClientValidationRule;
}
}
Client side code:
$.validator.addMethod("mincustomlength", function (value, element, params) {
var conditionalId = $.validator.getId(element, params.prop);
var minLength = parseInt($("#" + conditionalId).val());
if (value.length < minLength) {
var message = $(element).attr('data-val-mincustomlength');
$.validator.messages.mincustomlength = $.format(message, minLength);
return false;
}
return true;
});
$.validator.unobtrusive.adapters.add('mincustomlength', ['prop'], function (options) {
options.rules['mincustomlength'] = options.params;
if (options.message != null) {
$.validator.messages.mincustomlength = options.message;
}
});
This will replace {0} with a value from MinLenthDestURI textbox when validation is done.
Hope it helps!
I am using CompareAttribute in MVC3 and its working fine. But I want to use case insensitive classCode. Is there any way to get that working
Thanks in Advance
[CompareAttribute("ClassCode", ErrorMessageResourceName = "ClassCode_DontMatch", ErrorMessageResourceType = typeof(Resources.Class))]
public string ConfirmClassCode {get; set; }
A little late to the party, but here is an implementation I just wrote that also includes support for client-side validation using the IClientValidatable interface. You could use Darin Dimitrov's answer as a starting point as well, I just already had some of this.
Server-Side Validation:
//Create your custom validation attribute
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public class CompareStrings : ValidationAttribute, IClientValidatable
{
private const string _defaultErrorMessage = "{0} must match {1}";
public string OtherPropertyName { get; set; }
public bool IgnoreCase { get; set; }
public CompareStrings(string otherPropertyName)
: base(_defaultErrorMessage)
{
if (String.IsNullOrWhiteSpace(otherPropertyName)) throw new ArgumentNullException("OtherPropertyName must be set.");
OtherPropertyName = otherPropertyName;
}
public override string FormatErrorMessage(string name)
{
return String.Format(ErrorMessage, name, OtherPropertyName);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
string otherPropVal = validationContext.ObjectInstance.GetType().GetProperty(OtherPropertyName).GetValue(validationContext.ObjectInstance, null) as string;
//Convert nulls to empty strings and trim spaces off the result
string valString = (value as string ?? String.Empty).Trim();
string otherPropValString = (otherPropVal ?? String.Empty).Trim();
bool isMatch = String.Compare(valString, otherPropValString, IgnoreCase) == 0;
if (isMatch)
return ValidationResult.Success;
else
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
Client-Side Validation
//...continuation of CompareStrings class
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
return new[] { new ModelClientValidationCompareStringsRule(FormatErrorMessage(metadata.GetDisplayName()), OtherPropertyName, IgnoreCase) };
}
}
Define ModelClientValidationCompareStringsRule which is used (above) to pass the attribute's properties to the client-side script.
public class ModelClientValidationCompareStringsRule : ModelClientValidationRule
{
public ModelClientValidationCompareStringsRule(string errorMessage, string otherProperty, bool ignoreCase)
{
ErrorMessage = errorMessage; //The error message to display when invalid. Note we used FormatErrorMessage above to ensure this matches the server-side result.
ValidationType = "comparestrings"; //Choose a unique name for your validator on the client side. This doesn't map to anything on the server side.
ValidationParameters.Add("otherprop", otherProperty); //Pass the name of the property to compare to
ValidationParameters.Add("ignorecase", ignoreCase.ToString().ToLower()); //And whether to ignore casing
}
}
Javascript:
(function ($) {
//Add an adapter for our validator. This maps the data from the ModelClientValidationCompareStringsRule
//we defined above, to the validation plugin. Make sure to use the same name as we chose for the ValidationType property ("comparestrings")
$.validator.unobtrusive.adapters.add("comparestrings", ["otherprop", "ignorecase"],
function (options) {
options.rules["comparestrings"] = {
otherPropName: options.params.otherprop,
ignoreCase: options.params.ignorecase == "true"
};
options.messages["comparestrings"] = options.message;
});
//Add the method, again using the "comparestrings" name, that actually performs the client-side validation to the page's validator
$.validator.addMethod("comparestrings", function (value, element, params) {
//element is the element we are validating and value is its value
//Get the MVC-generated prefix of element
//(E.G. "MyViewModel_" from id="MyViewModel_CompareEmail"
var modelPrefix = getModelIDPrefix($(element).prop("id"));
//otherPropName is just the name of the property but we need to find
//its associated element to get its value. So concatenate element's
//modelPrefix with the other property name to get the full MVC-generated ID. If your elements use your own, overridden IDs, you'd have to make some modifications to allow this code to find them (e.g. finding by the name attribute)
var $otherPropElem = $("#" + modelPrefix + params.otherPropName);
var otherPropValue = getElemValue($otherPropElem);
//Note: Logic for comparing strings needs to match what it does on the server side
//Trim values
value = $.trim(value);
otherPropValue = $.trim(otherPropValue);
//If ignoring case, lower both values
if (params.ignoreCase) {
value = value.toLowerCase();
otherPropValue = otherPropValue.toLowerCase();
}
//compare the values
var isMatch = value == otherPropValue;
return isMatch;
});
function getElemValue(element){
var value;
var $elem = $(element);
//Probably wouldn't use checkboxes or radio buttons with
//comparestrings, but this method can be used for other validators too
if($elem.is(":checkbox") || $elem.is(":radio") == "radio")
value = $elem.prop("checked") ? "true" : "false";
else
value = $elem.val();
return value;
}
//Gets the MVC-generated prefix for a field by returning the given string
//up to and including the last underscore character
function getModelIDPrefix(fieldID) {
return fieldID.substr(0, fieldID.lastIndexOf("_") + 1);
}
}(jQuery));
Usage is standard:
public string EmailAddress { get; set; }
[CompareStrings("EmailAddress", ErrorMessage = "The email addresses do not match", IgnoreCase=true)]
public string EmailAddressConfirm { get; set; }
This plugs into the Unobtrusive Validation framework, so you need to already have that installed and working. At the time of writing I am on Microsoft.jQuery.Unobtrusive.Validation v 3.0.0.
You could write a custom attribute that will perform the case insensitive comparison:
public class CaseInsensitiveCompareAttribute : System.Web.Mvc.CompareAttribute
{
public CaseInsensitiveCompareAttribute(string otherProperty)
: base(otherProperty)
{ }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(this.OtherProperty);
if (property == null)
{
return new ValidationResult(string.Format(CultureInfo.CurrentCulture, "Unknown property {0}", this.OtherProperty));
}
var otherValue = property.GetValue(validationContext.ObjectInstance, null) as string;
if (string.Equals(value as string, otherValue, StringComparison.OrdinalIgnoreCase))
{
return null;
}
return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
}
}
and then decorate your view model property with it:
[CaseInsensitiveCompare("ClassCode", ErrorMessageResourceName = "ClassCode_DontMatch", ErrorMessageResourceType = typeof(Resources.Class))]
public string ConfirmClassCode { get; set; }
For client side validation, put this code below in document ready-
jQuery.validator.addMethod("ignoredCaseEqualTo", function (value, element, param) {
return this.optional(element) || value.toLowerCase() === $(param).val().toLowerCase();
}, "__Your Validation message___");
$("#EmailAddress").rules("add", {
ignoredCaseEqualTo: "#EmailAddressConfirm"
});
this code adds new validation rule of case insensitive comparison.
Might not be an optimum way, but this will do your job for client side validation.