Custom DataAnnotation in asp.net mvc not working - asp.net

[Required]
[ValidatePasswordLength]
[DataType(DataType.Password)]
[Display(Name = "Password")]
[Minimumthreenumbers]
public string Password { get; set; }
public class MinimumthreenumbersAttribute : ValidationAttribute
{
private const string _defaultErrorMessage = "There should be minimum three letters in the string";
private string Otherpassword;
public MinimumthreenumbersAttribute() : base(_defaultErrorMessage)
{
}
public override bool IsValid(object value)
{
string i = value.ToString();
string jobId = i;
int digitsCount = 0;
foreach (char c in jobId)
{
if (Char.IsDigit(c))
digitsCount++;
}
if (digitsCount > 3)
{
return true;
}
else
{
return false;
}
}
}
The above is the custom attribute implementation class .The above code actually has to validate the password to check for minimum 3 numbers.If the user entered password has less than 3 digits it should throw an error.This is the requirement. But is not working as expected. Any ideas on how to make the above code working? I have tried for some time but still it is not working.

Not sure that will be enough to make your code "work", but at least this will simplify your code.
public class MinimumthreenumbersAttribute : ValidationAttribute
{
public MinimumthreenumbersAttribute() : base("There should be minimum three letters in the string")
{
}
public override bool IsValid(object value)
{
return value != null &&
value.ToString()
.Where(Char.IsDigit)
.Count() >=3
}
}

You could just use a RegularExpressionAttribute to ensure there are three digits:
[RegularExpression(#".*\d.*\d.*\d.*")]
public string Password { get; set; }

Related

ASP.NET GET request always returns blank

I'm trying to make an endpoint to return a JSON response. I've tried narrowing it down to just the object and all I receive is {} or [{}] as a response. After debugging I confirmed that the object was being created correctly but when returning the response it was always blank. Below is simplified code but still has the same issue. Any ideas what I'm doing wrong?
[Route("{application}")]
[HttpGet]
public IActionResult Get(string application)
{
List<RequestedSetting> requestedSettings = new List<RequestedSetting>();
RequestedSetting rs = new RequestedSetting("foo", "bar");
requestedSettings.Add(rs);
return Json(requestedSettings);
}
public class RequestedSetting
{
public string Name;
public string Value;
public RequestedSetting(string name, string value)
{
Name = name;
Value = value;
}
}
I've also tried this:
[Route("{application}")]
[HttpGet]
public List<RequestedSetting> Get(string application)
{
List<RequestedSetting> requestedSettings = new List<RequestedSetting>();
RequestedSetting rs = new RequestedSetting("foo", "bar");
requestedSettings.Add(rs);
return requestedSettings;
}
Fields are not supported for serialisation in System.Text.Json. see the docs for more info.
Instead, change your class to use properties:
public class RequestedSetting
{
public string Name { get; set; }
public string Value { get; set; }
public RequestedSetting(string name, string value)
{
Name = name;
Value = value;
}
}

List <T> store large amounts of data , Not enough memory

public class ListKeywords
{
public int ID { set; get; }
public string Keyword { set; get; } //关键词
public string Language { set; get; } //语种
public int WordCount { set; get; } //单词数
public int WordLength { set; get; } // 字符数
public int Status { set; get; } //采集状态 0-未采集 1-采集成功 2-保存失败 3-保存成功 4-发布失败 5-发布成功
public bool Taken { set; get; }
public bool FTPStatus { set; get; }
public bool DBStatus { set; get; }
public string UrlName { set; get; }
public ListKeywords()
{
}
public ListKeywords(string keyword)
{
this.Keyword = keyword;
}
}
List<string> lines = new List<string>();
List<ListKeywords> keywordsList = new List<ListKeywords>();
using (StreamReader sr = File.OpenText(filePath))
{
string s = String.Empty;
while ((s = sr.ReadLine()) != null)
{
//lines.Add(s); //Operating normally
eywordsList.Add(new ListKeywords("some keywords")); // Operating normally
keywordsList.Add(new ListKeywords(s)); // it will be out of memeory
}
}
In text file, have 1,000,000 line data, if i use above code to load the large data to list< keywordsList >, it will raises an OutOfMemoryException, but if i load it to list< string >, it run normally. How to solved it ?
Instead of using a List maybe try using an IEnumerable w/ yield?
static IEnumerable<ListKeywords> Keywords()
{
using (StreamReader sr = File.OpenText(path))
{
string s = String.Empty;
while ((s = sr.ReadLine()) != null)
{
yield return new ListKeywords(s);
}
}
}
Note that Jon Skeet's C# in Depth offers a great explanation about this in Chapter 6. I imagine he also has some articles or posts on StackOverflow about this topic. As he points out, you want to be careful about modifying this method to pass in a StreamReader (or TextReader as is used in his example) as you would want to take ownership of the reader so it will be properly disposed of. Rather, you would want to pass in a Func<StreamReader> if you have such a need. Another interesting note he adds here - which I will point out because there are some edge cases where the reader will not actually be properly disposed of even if you don't allow the reader to be provided by the caller - it's possible for the caller to abuse the IEnumerable<ListKeywords> by doing something like Keywords().GetEnumerator() - this could result in a memory leak and could even potentially cause security issues if you have security-related code which relies on the using statement to clean up the resource.

ASP MVC Data Annotation - How to verify that two model properties are different using CompareAttribute

I regularly use the CompareAttribute data annotation in my models to validate that two fields are equal. For example, most of us use it to compare a password and a confirm password field.
It may seems trivial but I wonder how to use such annotation to compare that two fields are differents. For instance, I would like to verify that the password is different from the username.
For more complex validations, I know I have to use custom validators, but I was just wondering if there was something built-in for that.
Thank you.
You have two choices here, create your own ValidationAttribute inheriting from CompareAttribute or inheriting from ValidationAttribute.
1) Custom ValidationAttribute inherit from CompareAttribute
public class NotEqualAttribute : CompareAttribute
{
public string BasePropertyName { get; private set; }
private const string DefaultErrorMessage = "'{0}' and '{1}' must not be the same.";
public MyCustomCompareAttribut(string otherProperty)
: base(otherProperty)
{
BasePropertyName = otherProperty;
}
public override string FormatErrorMessage(string name)
{
return string.Format(DefaultErrorMessage, name, BasePropertyName);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var result = base.IsValid(value, validationContext);
if (result == null)
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
}
2)Custom ValidationAttribute inherit from ValidationAttribute
public class NotEqualAttribute : ValidationAttribute
{
private const string DefaultErrorMessage = "'{0}' and '{1}' must not be the same.";
public string BasePropertyName { get; private set; }
public NotEqualAttribute(string basePropertyName)
: base(DefaultErrorMessage)
{
BasePropertyName = basePropertyName;
}
public override string FormatErrorMessage(string name)
{
return string.Format(DefaultErrorMessage, name, BasePropertyName);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(BasePropertyName);
if (property == null)
{
return new ValidationResult(
string.Format(
CultureInfo.CurrentCulture, "{0} is invalid property", BasePropertyName
)
);
}
var otherValue = property.GetValue(validationContext.ObjectInstance, null);
if (object.Equals(value, otherValue))
{
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
}
Then you can use either one like:
public class YourModelClass
{
public string PropertyA{ get; set; }
[NotEqual("PropertyA")]
public string PropertyB{ get; set; }
}

ASP.NET MVC - Choose which validation annotations to use

I have a model with properties that look like this:
public class YourDetails {
[Required(ErrorMessage = "Code is required")]
[StringLength(10, ErrorMessage = "Code length is wrong", MinimumLength = 2)]
[Range(0, int.MaxValue, ErrorMessage = "Please enter a value bigger than {1}")]
public int Code { get; set; }
}
The UI validation is setup the usual out of the box way with unobtrusive JS validation plugin.
The issue: I have 2 navigation actions, back and next. Next is fine, validation fires when things are wrong, and when things are right i.e. .isValid() returns true, the data is passed to the DB service etc etc.
However when I press 'back' I have a requirement to validate the form/ViewModel differently prior to saving. I.e. make sure Code is a positive integer, but don't bother with the Required or StringLength validation.
So basically I want to validate fully on Next but partially on Back. Is that possible?
When I've done something similar in the past the easiest way i found was to use fluent validation http://fluentvalidation.codeplex.com/wikipage?title=mvc. You can pass parameters to the validator and switch to different rule sets.
I've used the following conditional "Required" & "StringLength" attributes in the past and they work well.
Required If Attribute:
using System;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
namespace Website.Core.Mvc.DataAnnotations
{
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)]
public class RequiredIfAttribute : RequiredAttribute
{
public string OtherProperty { get; set; }
public object OtherPropertyValue { get; set; }
public RequiredIfAttribute(string otherProperty, object value)
: base()
{
OtherProperty = otherProperty;
OtherPropertyValue = value;
}
private object _TypeId = new object();
public override object TypeId
{
get
{
return _TypeId;
}
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
PropertyInfo property = validationContext.ObjectType.GetProperty(this.OtherProperty);
if (property == null)
{
return new ValidationResult(this.OtherProperty + " not found");
}
// Get
object actualOtherPropertyValue = property.GetValue(validationContext.ObjectInstance, null);
// If the other property matches the expected value then validate as normal
if (IsRequired(OtherPropertyValue, actualOtherPropertyValue))
{
// Call base and validate required as normal
ValidationResult isValid = base.IsValid(value, validationContext);
return isValid;
}
return ValidationResult.Success;
}
protected virtual bool IsRequired(object otherPropertyValue, object actualOtherPropertyValue)
{
return object.Equals(OtherPropertyValue, actualOtherPropertyValue);
}
}
}
String Length If Attribute:
using System.ComponentModel.DataAnnotations;
using System.Reflection;
namespace Website.Core.Mvc.DataAnnotations
{
public class StringLengthIfAttribute : StringLengthAttribute
{
public string OtherProperty { get; set; }
public object OtherPropertyValue { get; set; }
public StringLengthIfAttribute(int maximumLength, string otherProperty, object value)
: base(maximumLength)
{
OtherProperty = otherProperty;
OtherPropertyValue = value;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
PropertyInfo property = validationContext.ObjectType.GetProperty(this.OtherProperty);
if (property == null)
{
return new ValidationResult(this.OtherProperty + " not found");
}
// Get
object actualOtherPropertyValue = property.GetValue(validationContext.ObjectInstance, null);
// If the other property matches the expected value then validate as normal
if (object.Equals(OtherPropertyValue, actualOtherPropertyValue))
{
// Call base and validate required as normal
return base.IsValid(value, validationContext);
}
return null;
}
}
}
Example Usage:
public class MyModel
{
[RequiredIf("IsBack", false)]
public string Name { get; set; }
public bool IsBack { get; set; }
}

xml serialization error on bool types

I am trying to find out how to solve the problem for serializing a type of bool from a camel case string.
I have the following xml
<Root>
<BoolElement>
False
</BoolElement>
</Root>
and the following class
[XmlRoot("Root")]
public class RootObj{
[XmlElement("BoolElement")]
public bool BoolElement{get;set;}
}
this will produce an error.
If I use the same class and rename the "False" to "false" it will work. The problem is that I can't edit the xml.
Does anyone know how can I solve this?
You could use a backing field to aid for the deserialization of this invalid XML (I say invalid because according to the xsd:boolean schema False is an invalid value):
[XmlRoot("Root")]
public class RootObj
{
[XmlElement("BoolElement")]
public string BackingBoolElement
{
set
{
BoolElement = bool.Parse(value.ToLower());
}
get
{
return BoolElement.ToString();
}
}
[XmlIgnore]
public bool BoolElement { get; set; }
}
False is not a valid value for an xsd:boolean (but as you note false and 0 are) - if you cannot change the source data, then you could have a separate property purely for XML serialisation:
[XmlRoot("Root")]
public class RootObj{
[XmlElement("BoolElement")]
public string BoolElementForSerialization
{
get
{
return (this.BoolElement ? "True" : "False");
}
set
{
this.BoolElement = (string.Compare(value, "false", StringComparison.OrdinalIgnoreCase) != 0);
}
}
[XmlIgnore]
public bool BoolElement{get;set;}
}
I created a new Boolean type that can deserialize from any string. It may not be perfect but it suited my needs at the time.
For the class you want to use simply change the data type from bool to SerializableBoolean:
[XmlRoot("Root")]
public class RootObj{
[XmlElement("BoolElement")]
public SerializableBoolean BoolElement{get;set;}
}
You can then use the BoolElement property like any normal bool data type:
RootObj myObj = new RootObj();
if (myObj.BoolElement) { ... }
Here is the code for the SerializableBoolean class, note this code only handles deserializing, serializing to xml wasn't required for my purposes and so not implemented.
[System.Diagnostics.DebuggerDisplay("{Value}")]
public struct SerializableBoolean: System.Xml.Serialization.IXmlSerializable
{
private bool Value { get; set; }
public override bool Equals(object obj)
{
if (obj is string stringBoolean)
{
bool.TryParse(stringBoolean, out bool boolean);
return Value == boolean;
}
else if (obj is bool boolean)
{
return Value == boolean;
}
else if (obj is SerializableBoolean serializableBoolean)
{
return Value == serializableBoolean.Value;
}
else
{
return Value == Convert.ToBoolean(obj);
}
}
public override int GetHashCode()
{
return -1937169414 + Value.GetHashCode();
}
public XmlSchema GetSchema()
{
throw new NotImplementedException();
}
public void ReadXml(XmlReader reader)
{
Value = Convert.ToBoolean(reader.ReadElementContentAsString());
}
public void WriteXml(XmlWriter writer)
{
throw new NotImplementedException();
}
public static bool operator ==(SerializableBoolean obj1, bool obj2)
{
return obj1.Value.Equals(obj2);
}
public static bool operator !=(SerializableBoolean obj1, bool obj2)
{
return !obj1.Value.Equals(obj2);
}
public static implicit operator SerializableBoolean(string value)
{
return new SerializableBoolean() { Value = Convert.ToBoolean(value) };
}
public static implicit operator SerializableBoolean(bool value)
{
return new SerializableBoolean() { Value = value };
}
public static implicit operator bool(SerializableBoolean b)
{
return b.Value;
}
}

Resources