hi i'm googling around about this problem but i didn't find any usfull about this.
I want to deni the set of an property if the Validation per DataAnnotations fails
Could you please tell me what i miss in my code?
Model Codesnip
private string _firstname;
public string Firstname
{
get { return _firstname; }
set
{
_firstname = value;
RaisePropertyChanged(() => Reg(() => Firstname));
}
}
ViewModel Codesnip
[Required]
[RegularExpression(#"^[a-zA-ZäöüßÄÖÜß''-'\s]{2,40}$")]
public string Name
{
get { return currentperson.Name; }
set
{
currentperson.Name = value;
RaisePropertyChanged(() => Reg(() => Name));
}
}
View Codesnip
<TextBox HorizontalAlignment="Left" VerticalAlignment="Top" Width="120" Text="{Binding Firstname,UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
any help would be greatly appreciated
EDIT: adding Validation Class
public class VMValidation : VMBase, IDataErrorInfo
{
private Dictionary<string, string> ErrorList = new Dictionary<string, string>();
/// <summary>
/// Gets a value indicating whether or not this domain object is valid.
/// </summary>
public bool IsVailid
{
get { return (ErrorList.Count == 0) ? true : false; }
}
#region IDataErrorInfo
/// <summary>
/// Gets an error message indicating what is wrong with this domain object.
/// The default is an empty string ("").
/// </summary>
public string Error
{
get { return getErrors(); } // noch keine richtige methode für gefunden müsste so aber auch funktionieren
}
/// <summary>
/// dies ist eine methode die durch einen Event (noch unbekannt welcher)
/// ausgelöst wird und den Propertynamen schon mit übergeben bekommt
/// </summary>
/// <param name="propertyName">Name der Property z.B FirstName</param>
/// <returns>The error message for the property.
/// The default is an empty string ("").</returns>
public string this[string propertyName]
{
get { return OnValidate(propertyName); }
}
private string getErrors()
{
string Error = "";
foreach (KeyValuePair<string, string> error in ErrorList)
{
Error += error.Value;
Error += Environment.NewLine;
}
return Error;
}
/// <summary>
/// Validates current instance properties using Data Annotations.
/// </summary>
/// <param name="propertyName">Name der Property</param>
/// <returns>ErrorMsg</returns>
protected virtual string OnValidate(string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
throw new ArgumentException("Invalid property name", propertyName);
string error = string.Empty;
var value = this.GetType().GetProperty(propertyName).GetValue(this, null);
var results = new List<ValidationResult>(1);
var context = new ValidationContext(this, null, null) { MemberName = propertyName };
var result = Validator.TryValidateProperty(value, context, results);
if (!result)
{
var validationResult = results.First();
error = validationResult.ErrorMessage;
}
if (error.Length > 0)
{
if (!ErrorList.ContainsKey(propertyName))
ErrorList.Add(propertyName, error);
}
else
if (!ErrorList.ContainsKey(propertyName))
ErrorList.Remove(propertyName);
return error;
}
public bool VailidValue(string propertyName, object value)
{
var results = new List<ValidationResult>(1);
var context = new ValidationContext(this, null, null) { MemberName = propertyName };
var result = Validator.TryValidateProperty(value, context, results);
return result;
}
#endregion //IDataErrorInfo
}
Try creating a validation method, and only setting the value if the result is true.
public string Name
{
get { return currentperson.Name; }
set
{
if (ValidateProperty("Name", value))
{
currentperson.Name = value;
RaisePropertyChanged(() => Reg(() => Name));
}
}
}
protected bool ValidateProperty(string propertyName, object value)
{
var results = new List<ValidationResult>();
bool isValid = Validator.TryValidateProperty(
value,
new ValidationContext(this, null)
{
MemberName = propertyName
},
results);
// Do what you want with the validation results
return isValid;
}
i changed my VMValidation
public class VMValidation : VMBase, IDataErrorInfo
{
private Dictionary<string, string> ErrorList = new Dictionary<string, string>();
public bool IsVailid
{
get { return (ErrorList.Count == 0) ? true : false; }
}
#region IDataErrorInfo
public string Error
{
get { return getErrors(); } // noch keine richtige methode für gefunden müsste so aber auch funktionieren
}
public string this[string propertyName]
{
get { return OnValidate(propertyName); }
}
private string getErrors()
{
string Error = "";
foreach (KeyValuePair<string, string> error in ErrorList)
{
Error += error.Value;
Error += Environment.NewLine;
}
return Error;
}
protected virtual string OnValidate(string propertyName)
{
string error =String.Empty;
if (ErrorList.ContainsKey(propertyName))
error = ErrorList[propertyName];
return error;
}
public bool VailidateValue(string propertyName, object value)
{
if (string.IsNullOrEmpty(propertyName))
{
Logger.ERRROR(propertyName, "Invalid property name");
throw new ArgumentException("Invalid property name", propertyName);
}
var results = new List<ValidationResult>(1);
var context = new ValidationContext(this, null, null) { MemberName = propertyName };
var result = Validator.TryValidateProperty(value, context, results);
if (!result)
{
var validationResult = results.First();
string error = validationResult.ErrorMessage;
if (error.Length > 0)
{
if (!ErrorList.ContainsKey(propertyName))
ErrorList.Add(propertyName, error);
}
}
else
if (ErrorList.ContainsKey(propertyName))
ErrorList.Remove(propertyName);
return result;
}
#endregion //IDataErrorInfo
}
and do what Alyce suggested
[Required]
[RegularExpression(#"^[a-zA-ZäöüßÄÖÜß''-'\s]{2,40}$")]
public string Name
{
get
{
if (currentperson == null)
return "";
return currentperson.Name;
}
set
{
if (VailidateValue("Name", value))
{
currentperson.Name = value;
}
RaisePropertyChanged(() => Reg(() => Name)); //you have to Raise because otherwise you won't get the Error :)
}
}
Related
Below is the code for a simple JsonModelBinder I created for an ASP.NET Core Mvc app. Is there a simple way to recursively validate the model, its properties and the properties of its properties and so on?
JsonModelBinder
public class JsonModelBinder : IModelBinder
{
static readonly JsonSerializerSettings settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
};
public Task BindModelAsync(ModelBindingContext bindingContext)
{
try
{
var json = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).Values;
if (json.Count > 0)
{
var model = JsonConvert.DeserializeObject(json, bindingContext.ModelType, settings);
// TODO: Validate complex model
bindingContext.Result = ModelBindingResult.Success(model);
}
else
{
bindingContext.Result = ModelBindingResult.Success(null);
}
}
catch (JsonException ex)
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex.Message);
bindingContext.Result = ModelBindingResult.Failed();
}
return Task.CompletedTask;
}
}
Example model
public class Foo {
[Required]
public string Name { get; set; }
public List<Bar> Bars { get; set; }
public Baz Baz { get; set; }
}
public class Bar {
[Required]
public string Name { get; set; }
}
public class Baz {
[Required]
public string Name { get; set; }
}
Controller action
public async Task<IActionResult> Edit(Guid id, [Required, ModelBinder(typeof(JsonModelBinder))] Foo foo) {
if (ModelState.IsValid) {
// Do stuff
}
else {
return View(foo);
}
}
I created the following recursive data annotations validator, and used it like below. Based on some feedback I received, it appears that there is nothing like this built into .NET Core at this time.
Recursive data annotations validator:
public static class RecursiveValidator
{
/// <summary>
/// Recursively validates <paramref name="instance"/>.
/// </summary>
/// <param name="instance"></param>
/// <param name="prefix"></param>
/// <param name="validationContext"></param>
/// <param name="results"></param>
/// <param name="validateAllProperties"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"><paramref name="instance"/> is null</exception>
public static bool TryValidateObject(object instance, IServiceProvider serviceProvider, IDictionary<object, object> items, ICollection<ValidationResult> results, bool validateAllProperties, string prefix)
{
if (instance is null)
{
throw new ArgumentNullException(nameof(instance));
}
var tempResults = new List<ValidationResult>();
ValidationContext validationContext = new ValidationContext(instance, serviceProvider, items);
var isValid = Validator.TryValidateObject(instance, validationContext, tempResults, validateAllProperties: validateAllProperties);
foreach (var item in tempResults)
{
IEnumerable<string> memberNames = item.MemberNames.Select(name => (!string.IsNullOrEmpty(prefix) ? prefix + "." : "") + name);
results.Add(new ValidationResult(item.ErrorMessage, memberNames));
}
foreach (var prop in instance.GetType().GetProperties())
{
if (prop.GetSetMethod() == null)
{
continue;
}
else if (prop.PropertyType.IsClass && prop.PropertyType != typeof(string))
{
var value = prop.GetValue(instance);
if (value == null)
{
continue;
}
else if (value is IEnumerable<object> list)
{
var memberPrefix = (!string.IsNullOrEmpty(prefix) ? prefix + "." : "") + prop.Name;
int i = 0;
foreach (var item in list)
{
if (!TryValidateObject(item, serviceProvider, items, results, validateAllProperties: validateAllProperties, prefix: $"{memberPrefix}[{i}]"))
{
isValid = false;
}
i++;
}
}
else
{
var memberPrefix = (!string.IsNullOrEmpty(prefix) ? prefix + "." : "") + prop.Name;
if (!TryValidateObject(value, serviceProvider, items, results, validateAllProperties: validateAllProperties, prefix: memberPrefix))
{
isValid = false;
}
}
}
}
return isValid;
}
}
Example of using it to add model state errors to the bindingContext:
var validationResults = new List<ValidationResult>();
if (!RecursiveValidator.TryValidateObject(model, bindingContext.HttpContext.RequestServices, null, validationResults, validateAllProperties: true, prefix: bindingContext.ModelName))
{
foreach (var result in validationResults)
{
foreach (var member in result.MemberNames)
{
bindingContext.ModelState.AddModelError(member, result.ErrorMessage);
}
}
}
I have this code:
wordGrid.BindingContext = AS.phrase;
AS.phrase = new PSCViewModel() { English = "abcd" };
AS.phrase.English = "JJJJ";
With the setting of BindingContext on the first line I don't see anything in my view. With it after it works and I see "JJJJ".
Here is my viewModel:
public class PSCViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
int id;
string english;
public PSCViewModel()
{
}
public int Id
{
get { return id; }
set
{
if (value != id)
{
id = value;
onPropertyChanged("ID");
}
}
}
public string English
{
get { return english; }
set
{
if (value != english)
{
english = value;
onPropertyChanged("English");
}
}
}
private void onPropertyChanged(string v)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(v));
}
}
}
Can anyone see why the change to the English field would not cause the new value of JJJJ to be displayed?
this is how I implemented INotifyPropertyChanged.
public class Bindable : INotifyPropertyChanged
{
private Dictionary<string, object> _properties = new Dictionary<string, object>();
/// <summary>
/// Gets the value of a property
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <returns></returns>
protected T Get<T>([CallerMemberName] string name = null)
{
object value = null;
if (_properties.TryGetValue(name, out value))
return value == null ? default(T) : (T)value;
return default(T);
}
/// <summary>
/// Sets the value of a property
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <param name="name"></param>
protected void Set<T>(T value, [CallerMemberName] string name = null)
{
if (Equals(value, Get<T>(name)))
return;
_properties[name] = value;
OnPropertyChanged(name);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Here is a sample class describing how to use
public class Transaction : Bindable
{
public Transaction()
{
this.TransactionDate = DateTimeOffset.Now;
this.TransactionType = TransactionType.Add; //enum
this.Quantity = 0;
this.IsDeleted = false;
this.Item = null; //object defined elsewhere
}
public Guid Id { get { return Get<Guid>(); } private set { Set<Guid>(value); } }
public DateTimeOffset? TransactionDate { get { return Get<DateTimeOffset?>(); } set { Set<DateTimeOffset?>(value); } }
public TransactionType TransactionType { get { return Get<TransactionType>(); } set { Set<TransactionType>(value); } }
public double? Quantity { get { return Get<double?>(); } set { Set<double?>(value); } }
public bool? IsDeleted { get { return Get<bool?>(); } set { Set<bool?>(value); } }
public byte[] RowVersion { get { return Get<byte[]>(); } private set { Set<byte[]>(value); } }
public virtual Guid? ItemId { get { return Get<Guid?>(); } set { Set<Guid?>(value); } }
public virtual Item Item { get { return Get<Item>(); } set { Set<Item>(value); } }
}
You probably already found the definition in System.ComponentModel
It's all part of MVVM. Your ViewModel must implement INotifyPropertyChanged. There is only one event in it: PropertyChangedEventHandler PropertyChanged
I usually define a raise method in the ViewModel like this:
protected void RaisePropertyChanged(string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Then all the properties in the ViewModel must have they getter/setter like this:
public string AProperty
{
get { return aProperty;}
set
{
if(value != aProperty)
{
aProperty = value;
RaisePropertyChanged("AProperty");
}
}
}
Now, when you bind your View with the ViewModel, it will subscribe to PropertyChanged event an propagate the changes. That's it !
A good start would be to read up on the MVVM pattern and how to implement it in Xamarin Forms. Xamarin has their own tutorials on the topic such as this one:
https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/data_bindings_to_mvvm/
Basically what you do is create a ViewModel which acts as the BindingContext for the entire page. Within that ViewModel you define properties that are bound to your controls such as Labels, ListViews and TextBoxes. In your case the ViewModel would contain a string property called Phrase that is bound to the control called wordGrid.
public class PhraseViewModel
{
public string Phrase {get; set;}
}
Which can be bound in XAML to e.g. a Label like:
<Label Text="{Binding Phrase}" />
I'm running ASP WebAPI 2 and successfully installed Swashbuckle. I am trying to figure out how one defines what the default schema values are?
For example, on the Swagger live demo site they changed the default value of pet to "doggie". They also defined the allowable values for status. (Live Demo)
I managed to get this working by following what's on this link:
https://github.com/domaindrivendev/Swashbuckle/issues/69#issuecomment-53953785
In short this is what needs to be done:
Create the classes SwaggerDefaultValue and AddDefaultValues as described in the link. Some changes that I did:
public class SwaggerDefaultValue : Attribute
{
public string Name { get; set; }
public string Value { get; set; }
public SwaggerDefaultValue(string value)
{
this.Value = value;
}
public SwaggerDefaultValue(string name, string value) : this(value)
{
this.Name = name;
}
}
public class AddDefaultValues : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
IDictionary<string, object> parameterValuePairs =
GetParameterValuePairs(apiDescription.ActionDescriptor);
foreach (var param in operation.parameters)
{
var parameterValuePair = parameterValuePairs.FirstOrDefault(p => p.Key.IndexOf(param.name, StringComparison.InvariantCultureIgnoreCase) >= 0);
param.#default = parameterValuePair.Value;
}
}
private IDictionary<string, object> GetParameterValuePairs(HttpActionDescriptor actionDescriptor)
{
IDictionary<string, object> parameterValuePairs = new Dictionary<string, object>();
foreach (SwaggerDefaultValue defaultValue in actionDescriptor.GetCustomAttributes<SwaggerDefaultValue>())
{
parameterValuePairs.Add(defaultValue.Name, defaultValue.Value);
}
foreach (var parameter in actionDescriptor.GetParameters())
{
if (!parameter.ParameterType.IsPrimitive)
{
foreach (PropertyInfo property in parameter.ParameterType.GetProperties())
{
var defaultValue = GetDefaultValue(property);
if (defaultValue != null)
{
parameterValuePairs.Add(property.Name, defaultValue);
}
}
}
}
return parameterValuePairs;
}
private static object GetDefaultValue(PropertyInfo property)
{
var customAttribute = property.GetCustomAttributes<SwaggerDefaultValue>().FirstOrDefault();
if (customAttribute != null)
{
return customAttribute.Value;
}
return null;
}
}
Edit your SwaggerConfig and add the AddDefaultValues class to the OperationFilters:
GlobalConfiguration.Configuration
.EnableSwagger(c => {
...
c.OperationFilter<AddDefaultValues>()
...
});
Now for the parameters I want default values I just add the following:
public IHttpActionResult Put([FromBody]Pet pet)
{
...
return Ok();
}
public class Pet {
[SwaggerDefaultValue("doggie")]
public string Name { get; set; }
[SwaggerDefaultValue("available")]
public string Status;
...
}
Well the code of vgaspar.trivix did not work completly for me, the default values did not get set for the schema. Also i got an NullPointerException. I managed to get it working as intended by editing the Apply method and manipulated the schemaRegistry like this:
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
if (operation.parameters == null)
return;
IDictionary<string, object> parameterValuePairs =
GetParameterValuePairs(apiDescription.ActionDescriptor);
foreach (var param in operation.parameters)
{
if (param.schema != null && param.schema.#ref != null)
{
string schemaName = param.schema.#ref.Split('/').LastOrDefault();
if (schemaRegistry.Definitions.ContainsKey(schemaName))
foreach (var props in schemaRegistry.Definitions[schemaName].properties)
{
if (parameterValuePairs.ContainsKey(props.Key))
props.Value.#default = parameterValuePairs[props.Key];
}
}
var parameterValuePair = parameterValuePairs.FirstOrDefault(p => p.Key.IndexOf(param.name, StringComparison.InvariantCultureIgnoreCase) >= 0);
param.#default = parameterValuePair.Value;
}
}
An example Model Schema can be defined by implementing ISchemaFilter and registering it using the following:
httpConfig
.EnableSwagger(c =>
{
c.SchemaFilter<AddSchemaExamples>()
});
An example implementation is provided here:
public class AddSchemaExamples : ISchemaFilter
{
public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
{
if (type == typeof(Product))
{
schema.example = new Product
{
Id = 123,
Type = ProductType.Book,
Description = "Treasure Island",
UnitPrice = 10.0M
};
}
}
}
Source: https://github.com/domaindrivendev/Swashbuckle/issues/162
I know this thread is quite old, but I wanted to share my solution which creates a custom constructor just for the Swagger example schema.
In my model:
/// <summary>
/// Supply a custom constructor for Swagger where you can apply defaults to control the example schema.
/// The constructor must have one parameter of type System.Reflection.ParameterInfo[].
/// Note: Setting a property to null will prevent it from showing in the Swagger example.
/// </summary>System.Reflection.ParameterInfo[].
/// </summary>
public class SwaggerConstructor : Attribute { }
In SwaggerConfig.cs:
c.SchemaFilter<ApplySchemaVendorExtensions>();
The schema extension:
public class ApplySchemaVendorExtensions : ISchemaFilter
{
public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
{
ConstructorInfo constructor = type.GetConstructors().FirstOrDefault(c => c.GetCustomAttribute<SwaggerConstructor>() != null);
if (constructor != null)
{
schema.example = constructor.Invoke(new object[] { constructor.GetParameters() });
}
}
}
Usage:
[SwaggerConstructor]
public MyClass(System.Reflection.ParameterInfo[] decoy) : base()
{
MyProperty = false;
}
Stumbled across this just now, you can also set the tag in the XML documentation, in one of my models, I have this defined
/// <summary>
/// Note content
/// </summary>
/// <example>Any text for a note.</example>
public string Note { get; set; }
which ends up looking like this in the swagger documentation when selecting "Try It Now"
Hope that helps someone!
Using .NET 5 with Swashbuckle.AspNetCore 5.6.3, the only way I could get this to work efficiently is this:
public class ExampleDocFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
string ToCamelCase(string name) => char.ToLowerInvariant(name[0]) + name.Substring(1);
if (schema.Properties == null) return;
var setProperties = context.Type.GetProperties().ToList().Where(f => f.GetCustomAttribute<DefaultValueAttribute>() != null).Where(f => schema.Properties.Any(n => n.Key.Equals(ToCamelCase(f.Name)))).ToDictionary(f => f, f => f.GetCustomAttribute<DefaultValueAttribute>());
foreach (var prop in setProperties) schema.Properties[ToCamelCase(prop.Key.Name)].Example = OpenApiAnyFactory.CreateFor(schema.Properties[ToCamelCase(prop.Key.Name)], prop.Value.Value);
}
}
To use this - in your startup.cs:
services.AddSwaggerGen(swagger => {
...
swagger.SchemaFilter<ExampleDocFilter>();
});
I need to implement a RequiredIF validator which depends on two values, a checkbox and a value selected from a dropdown.
I need if possible something like
[RequiredIf("Property1", true,"Property2,"value", ErrorMessageResourceName = "ReqField", ErrorMessageResourceType = typeof(RegisterUser))]
Here is code to create your own RequiredIf and RequiredIfNot Custom Validator.
Simply add additional code if you want to check for 2 values.
RequiredIf
public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
private readonly RequiredAttribute _innerAttribute = new RequiredAttribute();
internal string _dependentProperty;
internal object _targetValue;
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
_dependentProperty = dependentProperty;
_targetValue = targetValue;
}
/// <summary>
/// Returns if the given validation result is valid. It checks if the RequiredIfAttribute needs to be validated
/// </summary>
/// <param name="value">Value of the control</param>
/// <param name="validationContext">Validation context</param>
/// <returns></returns>
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var field = validationContext.ObjectType.GetProperty(_dependentProperty);
if (field != null)
{
var dependentValue = field.GetValue(validationContext.ObjectInstance, null);
if ((dependentValue == null && _targetValue == null) || (dependentValue.ToString() == _targetValue.ToString()))
{
if (!_innerAttribute.IsValid(value))
{
return new ValidationResult(ErrorMessage);
}
}
return ValidationResult.Success;
}
else
{
throw new ValidationException("RequiredIf Dependant Property " + _dependentProperty + " does not exist");
}
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = ErrorMessageString,
ValidationType = "requiredif",
};
rule.ValidationParameters["dependentproperty"] = (context as ViewContext).ViewData.TemplateInfo.GetFullHtmlFieldId(_dependentProperty);
rule.ValidationParameters["desiredvalue"] = _targetValue is bool ? _targetValue.ToString().ToLower() : _targetValue;
yield return rule;
}
}
RequireIfNot:
public class RequiredIfNotAttribute : ValidationAttribute, IClientValidatable
{
private readonly RequiredAttribute _innerAttribute = new RequiredAttribute();
internal string _dependentProperty;
internal object _targetValue;
public RequiredIfNotAttribute(string dependentProperty, object targetValue)
{
_dependentProperty = dependentProperty;
_targetValue = targetValue;
}
/// <summary>
/// Returns if the given validation result is valid. It checks if the RequiredIfAttribute needs to be validated
/// </summary>
/// <param name="value">Value of the control</param>
/// <param name="validationContext">Validation context</param>
/// <returns></returns>
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var field = validationContext.ObjectType.GetProperty(_dependentProperty);
if (field != null)
{
var dependentValue = field.GetValue(validationContext.ObjectInstance, null);
if ((dependentValue == null && _targetValue == null) || (dependentValue.ToString() != _targetValue.ToString()))
{
if (!_innerAttribute.IsValid(value))
{
return new ValidationResult(ErrorMessage);
}
}
return ValidationResult.Success;
}
else
{
throw new ValidationException("RequiredIfNot Dependant Property " + _dependentProperty + " does not exist");
}
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = ErrorMessageString,
ValidationType = "requiredifnot",
};
rule.ValidationParameters["dependentproperty"] = (context as ViewContext).ViewData.TemplateInfo.GetFullHtmlFieldId(_dependentProperty);
rule.ValidationParameters["desiredvalue"] = _targetValue is bool ? _targetValue.ToString().ToLower() : _targetValue;
yield return rule;
}
}
Usage:
In your model use the following DataAnnotation.
[RequiredIf("IsRequired", true, ErrorMessage = "First Name is required.")]
You have to make custom validator. it is the bets way to achieve that. I hope this helps custom validator
I have windows phone 7 application and classes for database mapping.
A Poll class looks like that:
[Table]
public class Poll // :BaseModel
{
//private int _pid;
//private string _pdesc;
//private bool _pisopen;
//private string _pname;
//private bool _prandom;
//private string _qlastupdticks;
//private string _ticks;
[Column(DbType = "INT NOT NULL IDENTITY(1,1)", IsPrimaryKey = true, IsDbGenerated = true)]
public int id { get; set; }
[Column(DbType = "INT")]
public int pid { get; set; }
//{
// get { return _pid; }
// set { SetValue(ref _pid, value, GetPropertyName(MethodBase.GetCurrentMethod())); }
//}
[Column]
public string pdesc { get; set; }
//{
// get { return _pdesc; }
// set { SetValue(ref _pdesc, value, GetPropertyName(MethodBase.GetCurrentMethod())); }
//}
[Column]
public bool pisopen { get; set; }
//{
// get { return _pisopen; }
// set { SetValue(ref _pisopen, value, GetPropertyName(MethodBase.GetCurrentMethod())); }
//}
[Column]
public string pname { get; set; }
//{
// get { return _pname; }
// set { SetValue(ref _pname, value, GetPropertyName(MethodBase.GetCurrentMethod())); }
//}
[Column]
public bool prandom { get; set; }
//{
// get { return _prandom; }
// set { SetValue(ref _prandom, value, GetPropertyName(MethodBase.GetCurrentMethod())); }
//}
[Column(DbType = "NVARCHAR(255)")]
public string qlastupdticks { get; set; }
//{
// get { return _qlastupdticks; }
// set { SetValue(ref _qlastupdticks, value, GetPropertyName(MethodBase.GetCurrentMethod())); }
//}
[Column(DbType = "NVARCHAR(255)")]
public string ticks { get; set; }
//{
// get { return _ticks; }
// set { SetValue(ref _ticks, value, GetPropertyName(MethodBase.GetCurrentMethod())); }
//}
public override bool Equals(object obj)
{
var item = obj as Poll;
if (item != null)
{
Equals(item);
}
return false;
}
public bool Equals(Poll other)
{
if (ReferenceEquals(null, other)) return false;
return (other.pid == pid);
}
public override string ToString()
{
return string.Format("{0}_{1}", GetType().Name, pid);
}
public override int GetHashCode()
{
return ToString().ToUpper().GetHashCode();
}
}
Save method looks like that:
public bool RowsSave<T>(IEnumerable<T> entities, out string error)
{
error = string.Empty;
bool res;
var type = typeof (T);
using (var ctx = new PpaDataContext(_connectionString))
{
try
{
var entitesInDb = ctx.GetTable(type).Cast<T>().ToList();
var entitesForSave = new List<T>();
foreach (var entity in entities)
{
if (!entitesInDb.Contains(entity))
{
entitesForSave.Add(entity);
}
else
{
var index = entitesInDb.IndexOf(entity);
foreach (var prop in PropertiesGet(entity))
{
prop.SetValue(entitesInDb[index], prop.GetValue(entity, null), null);
}
}
}
if(entitesForSave.Count > 0)
{
ctx.GetTable(type).InsertAllOnSubmit(entitesForSave);
}
ctx.SubmitChanges();
res = true;
}
catch (Exception ex)
{
error = string.Format("{0}", ex.Message);
res = false;
}
}
return res;
}
When I try insert, each object inserts twice. What incorrect in this code?
The RowsSave method calling one time only.
This is a bit after the fact, but I've run into the exact same problem. This has to do with an error in overridden equality operators. In this specific case, the problem appears because the code does not return the value of the Equals function in:
public override bool Equals(object obj)
{
var item = obj as Poll;
if (item != null)
{
Equals(item);
}
return false;
}
That code will always return false. The corrected code should be:
public override bool Equals(object obj)
{
var item = obj as Poll;
if (item != null)
{
return Equals(item);
}
return false;
}
LINQ-to-SQL uses the object equals comparison after insertions. I'm not sure in what context and for what reason this happens, but if the comparison returns false, it runs the insertion again. If you're experiencing the same behavior, check any overridden equality methods for your entity for correctness.
I don't think you need the line:
ctx.SubmitChanges();
as an insert is being done on the line:
ctx.GetTable(type).InsertAllOnSubmit(entitesForSave);