MVC Model Custom Control Attribute - asp.net-mvc-4

I am using custom uniqueemail confirmation attribute like
public class UniqueEmailAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
WMContext db = new WMContext();
var userWithTheSameEmail = db.Users.SingleOrDefault(
u => u.email == (string)value);
return userWithTheSameEmail == null;
}
}
It is working fine while inserting user. But, when I try to update user it gives an error.
public class User
{
public int userID { get; set; }
[Required]
[Display(Name = "Name")]
public string name { get; set; }
[Required]
[Display(Name = "Email")]
[UniqueEmail(ErrorMessage = "This email is used by another user")]
public string email { get; set; }
}
Solution : I have changed the IsValid method like this :
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var model = (User)validationContext.ObjectInstance;
WMContext db = new WMContext();
var userWithTheSameEmail = db.Users.SingleOrDefault(
u => u.email == (string)value && u.userID!=model.userID);
if (userWithTheSameEmail!=null)
{
return new ValidationResult("Bu eposta adresi kullanılıyor.");
}
return ValidationResult.Success;
}

Related

Entity Framework Core no duplicate entries with ErrorMessage

What is the best way to avoid duplicate entries with MVC-EFC and ErrorMessage return
Model Test.cs
public class Test
{
public int Id { get; set; }
[Required(ErrorMessage = "Select a Name")]
[StringLength(50, ErrorMessage = "Max 50 character")]
public string Name { get; set; }
[StringLength(100, ErrorMessage = "Max 100 character")]
public string Text { get; set; }
}
I use a ApiController with "ErrorMessage" return.
For column "Name" i do not want duplicates and a ErrorMessage return like "Entry already available!"
What is the best way?
Try to implement custom ValidationAttribute
Customer ValidationAttribute
public class UniqueValidation : ValidationAttribute
{
private readonly string _errorMessage;
public UniqueValidation(string ErrorMessage)
{
_errorMessage = ErrorMessage;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (validationContext != null && typeof(IName).IsAssignableFrom(validationContext.ObjectType))
{
ApplicationDbContext db = validationContext.GetService(typeof(ApplicationDbContext)) as ApplicationDbContext;
IQueryable<IName> result = db.GetType().GetMethod("Set").MakeGenericMethod(validationContext.ObjectType).Invoke(db, null) as IQueryable<IName>;
var v = result.FirstOrDefault(u => u.Name == ((IName)validationContext.ObjectInstance).Name);
if (v != null)
{
return new ValidationResult(_errorMessage);
}
}
return ValidationResult.Success;
}
}
Use
public class File: IName
{
public int Id { get; set; }
[UniqueValidation("FileName is exist")]
public string Name { get; set; }
[InverseProperty("Avatar")]
public ICollection<ApplicationUser> Users { get; set; }
}
public interface IName
{
string Name { get; set; }
}

How can i change authentication type as phone number instead of user name on my web api?

My authentication is working fine on it is own but i need to use phoneNumber of users instead of user names.
There is my Provider class
using Identity.Infrastructure;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Identity.Providers
{
public class CustomOAuthProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
if (!user.EmailConfirmed)
{
context.SetError("invalid_grant", "User did not confirm email.");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
var ticket = new AuthenticationTicket(oAuthIdentity, null);
context.Validated(ticket);
}
}
}
in this class context is coming with only userName and Password,so it cant reach PhoneNumber even i send it as a parameter.I think problem will solve after if i can change
userManager.FindAsync(context.UserName, context.Password)
like this
userManager.FindAsync(context.PhoneNumber, context.Password)
VS doesn't allow me to interfere OAuthGrantResourceOwnerCredentialsContext
using Identity.Infrastructure;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Web.Http.Routing;
namespace Identity.Models
{
public class ModelFactory
{
private UrlHelper _UrlHelper;
private ApplicationUserManager _AppUserManager;
public ModelFactory(HttpRequestMessage request, ApplicationUserManager appUserManager)
{
_UrlHelper = new UrlHelper(request);
_AppUserManager = appUserManager;
}
public UserReturnModel Create(ApplicationUser appUser)
{
return new UserReturnModel
{
Url = _UrlHelper.Link("GetUserById", new { id = appUser.Id }),
Id = appUser.Id,
UserName = appUser.UserName,
FullName = string.Format("{0} {1}", appUser.FirstName, appUser.LastName),
Email = appUser.Email,
EmailConfirmed = true,
Level = appUser.Level,
JoinDate = appUser.JoinDate,
Roles = _AppUserManager.GetRolesAsync(appUser.Id).Result,
Claims = _AppUserManager.GetClaimsAsync(appUser.Id).Result,
PhoneNumber = appUser.PhoneNumber
};
}
public RoleReturnModel Create(IdentityRole appRole)
{
return new RoleReturnModel
{
Url = _UrlHelper.Link("GetRoleById", new { id = appRole.Id }),
Id = appRole.Id,
Name = appRole.Name
};
}
}
public class RoleReturnModel
{
public string Url { get; set; }
public string Id { get; set; }
public string Name { get; set; }
}
public class UserReturnModel
{
public string Url { get; set; }
public string Id { get; set; }
public string UserName { get; set; }
public string FullName { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public bool EmailConfirmed { get; set; }
public int Level { get; set; }
public DateTime JoinDate { get; set; }
public IList<string> Roles { get; set; }
public IList<System.Security.Claims.Claim> Claims { get; set; }
}
}
As result I stucked on authenticating with phoneNumber instead of userName and set deviceId as password
public override Task<ApplicationUser> FindAsync(string Phone, string password)
{
//Do your Stuff here
//return base.FindAsync(userName, password);
}
Overrride FIndAsync() in the IndentityConfig.cs

Custom Validation in MVC Model

I am having model as
public class GroupedIssueData
{
[Range(0, double.MaxValue, ErrorMessage = "Please enter valid number")]
public double IssueQty { get; set; }
public double ReqQty { get; set; }
public bool isSavings { get; set; }
}
This contains two properties as IssueQty and IsSaving, If the IsSaving is checked then IssueQty can be empty, If the IssueQty is not empty then IsSaving can be left blank. How can I validate this
My View is
<td>
#Html.DisplayFor(m => m.MaterialData[i].ReqQty)
#Html.HiddenFor(m => m.MaterialData[i].ReqQty)
</td>
<td>#Html.TextBoxFor(m => m.MaterialData[i].IssueQty, new { style = "width:70px" })#Html.ValidationMessageFor(m => m.MaterialData[i].IssueQty)</td>
<td class="text-center">#Html.CheckBoxFor(m => m.MaterialData[i].isSavings)</td>
And my Controller is
public async Task<ActionResult> GetWorkOrderMaterialDetails(IssueEntryModel m)
{
if (!ModelState.IsValid)
{
// redirect
}
var model = new IssueEntryModel();
}
How can I redirect to the if the Model is not valid. Do I need to redirect to same controller. I want to retain the entered data.
My View is
try this
`
[Required]
[Range(18, 100, ErrorMessage = "Please enter an age between 18 and 50")]
public int Age { get; set; }
[Required]
[StringLength(10)]
public int Mobile { get; set; }
[Range(typeof(decimal), "0.00", "15000.00")]
public decimal Total { get; set; } `
if (ModelState.IsValid)
{
//
}
return View(model);
Validation to the Model
Custom Validation Data Annotation Attribute
You can make a custom validation e.g. RequiredIfOtherFieldIsNullAttribute like described here:
How to validate one field related to another's value in ASP .NET MVC 3
public class RequiredIfOtherFieldIsNullAttribute : ValidationAttribute, IClientValidatable
{
private readonly string _otherProperty;
public RequiredIfOtherFieldIsNullAttribute(string otherProperty)
{
_otherProperty = otherProperty;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(_otherProperty);
if (property == null)
{
return new ValidationResult(string.Format(
CultureInfo.CurrentCulture,
"Unknown property {0}",
new[] { _otherProperty }
));
}
var otherPropertyValue = property.GetValue(validationContext.ObjectInstance, null);
if (otherPropertyValue == null || otherPropertyValue as string == string.Empty)
{
if (value == null || value as string == string.Empty)
{
return new ValidationResult(string.Format(
CultureInfo.CurrentCulture,
FormatErrorMessage(validationContext.DisplayName),
new[] { _otherProperty }
));
}
}
return null;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "requiredif",
};
rule.ValidationParameters.Add("other", _otherProperty);
yield return rule;
}
}
And use it like that:
[RequiredIfOtherFieldIsNull("IsSavings")]
public double IssueQty { get; set; }
[RequiredIfOtherFieldIsNull("IssueQty")]
public bool IsSavings { get; set; }
Use IValidatableObject
However your condition: If the IsSaving is checked then IssueQty can be empty, If the IssueQty is not empty then IsSaving can be left blank is bit confusing, but this might hint you anyways
public class GroupedIssueData : IValidatableObject
{
[Range(0, double.MaxValue, ErrorMessage = "Please enter valid number")]
public double IssueQty { get; set; }
public double ReqQty { get; set; }
public bool isSavings { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!isSavings && IssueQty == 0)
{
yield return new ValidationResult("Error Message");
}
}
}
public async Task<ActionResult> GetWorkOrderMaterialDetails(IssueEntryModel m)
{
if (!ModelState.IsValid)
{
return View(m);
// redirect
}
}

MVC RequiredIf Attribute - IsValid value parameter always null

I am implementing a RequiredIf validation attribute and the value being passed to the IsValid method is always null.
RequiredIfAttribute Class
public class RequiredIfAttribute : ValidationAttribute
{
private RequiredAttribute innerAttribute = new RequiredAttribute();
public string DependentProperty { get; set; }
public object TargetValue { get; set; }
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
this.DependentProperty = dependentProperty;
this.TargetValue = targetValue;
}
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
}
ViewModel
[Required]
[Display(Name = "Are You A Student?")]
public bool? IsStudent { get; set; }
[RequiredIf("IsStudent", true, ErrorMessage = "You must upload a photo of your student ID if you wish to register as a student.")]
[Display(Name = "Student ID")]
[FileExtensions("jpg|jpeg|png|pdf", ErrorMessage = "File is not in the correct format.")]
[MaxFileSize(2 * 1024 * 1024, ErrorMessage = "You may not upload files larger than 2 MB.")]
public HttpPostedFileBase StudentId { get; set; }
EditorTemplate
#model bool?
#using System.Web.Mvc;
#{
var options = new List<SelectListItem>
{
new SelectListItem { Text = "Yes", Value = "true", Selected = Model.HasValue && Model.Value },
new SelectListItem { Text = "No", Value = "false", Selected = Model.HasValue && Model.Value }
};
string defaultOption = null;
if (ViewData.ModelMetadata.IsNullableValueType)
{
defaultOption = "(Select)";
}
}
#Html.DropDownListFor(m => m, options, defaultOption)
Every time the form is submitted, the RequiredIf error message is thrown and I have a feeling it has to do with the null value I described initially. What am I doing wrong? Thanks!
NOTE: The HTML appears to be rendering properly, so I don't think that's the problem.
<select data-val="true" data-val-required="The Are You A Student? field is required." id="IsStudent" name="IsStudent"><option value="">(Select)</option>
<option value="true">Yes</option>
<option value="false">No</option>
</select>
Because this is your code -
public class RequiredIfAttribute : ValidationAttribute
{
private RequiredAttribute innerAttribute = new RequiredAttribute();
public string DependentProperty { get; set; }
public object TargetValue { get; set; }
public RequiredIfAttribute(string dependentProperty, object targetValue)
{
this.DependentProperty = dependentProperty;
this.TargetValue = targetValue;
}
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
}
You are using a RequriedAtrribute. So to simulate that it behaves life a RequiredIf you have to implement some logic that will check whether the target property value is true or false. But you are not doing that and returning just from the innerattribute. So it is just a mere Required not RequiredIf -
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
modify this function to do some checking like -
public override bool IsValid(object value)
{
//if the referred property is true then
return innerAttribute.IsValid(value);
//else
return True
}
I use the following code:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public abstract class StefAttribute : ValidationAttribute
{
public WDCIAttribute()
: base()
{
this.ErrorMessageResourceType = typeof(GlobalResources);
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]
public class StefRequiredIfAttribute : StefAttribute
{
private RequiredAttribute innerAttribute = new RequiredAttribute();
public string DependentProperty { get; set; }
public object TargetValue { get; set; }
public WDCIRequiredIfAttribute()
{
}
public WDCIRequiredIfAttribute(string dependentProperty, object targetValue)
: base()
{
this.DependentProperty = dependentProperty;
this.TargetValue = targetValue;
}
public override bool IsValid(object value)
{
return innerAttribute.IsValid(value);
}
}
public class RequiredIfValidator : DataAnnotationsModelValidator<StefRequiredIfAttribute>
{
public RequiredIfValidator(ModelMetadata metadata, ControllerContext context, StefRequiredIfAttribute attribute)
: base(metadata, context, attribute)
{
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
// get a reference to the property this validation depends upon
var field = Metadata.ContainerType.GetProperty(Attribute.DependentProperty);
if (field != null)
{
// get the value of the dependent property
object value = field.GetValue(container, null);
// compare the value against the target value
if (IsEqual(value) || (value == null && Attribute.TargetValue == null))
{
// match => means we should try validating this field
if (!Attribute.IsValid(Metadata.Model))
{
// validation failed - return an error
yield return new ModelValidationResult { Message = ErrorMessage };
}
}
}
}
private bool IsEqual(object dependentPropertyValue)
{
bool isEqual = false;
if (Attribute.TargetValue != null && Attribute.TargetValue.GetType().IsArray)
{
foreach (object o in (Array)Attribute.TargetValue)
{
isEqual = o.Equals(dependentPropertyValue);
if (isEqual)
{
break;
}
}
}
else
{
if (Attribute.TargetValue != null)
{
isEqual = Attribute.TargetValue.Equals(dependentPropertyValue);
}
}
return isEqual;
}
}
Which can be used in the model as follows:
public class PersonnelVM : EntityVM
{
// . . .
[DisplayName("Name")]
[StefRequiredIf("IndividualOrBulk", PersonnelType.Bulk, ErrorMessageResourceName = GlobalResourceLiterals.Name_Required)]
public string Name { get; set; }
[DisplayName("PersonnelType")]
public PersonnelType IndividualOrBulk { get; set; }
// . . .
}

Custom attribute to validate if mail is unique in asp.net mvc

I have the following model
public partial class OwnerData
{
public string firstname { get; set; }
public string lastname { get; set; }
[Required]
[StringLength(60)]
[EmailAddress]
[MailNotExists] //this is a custom function
public string email { get; set; }
}
the custom function MailNotExists is the following
namespace MyApp.CustomDataAnnotations
{
public class MailNotExists : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
DbContext db = new DbContext ();
var user = db.Users.FirstOrDefault(u => u.email == (string)value);
if (user == null)
return ValidationResult.Success;
else
return new ValidationResult("Mail already exists");
}
}
}
The model is used into the view to give the user the possibility to change his mail, trough
#Html.EditorForModel();
The html output returns into the editor also the already existing mail, if you want to change it the system first check if the mail is already present in the db trough the validator [MailNotExists] and everything is ok. The only problem is that if the user don't want to change the mail but only the firstname or lastname and then submit the form the validator returns an error because the mail already exists.
Any solution on how to by-pass the validator only in that particular case?
Well its a bit specific since i dont see primary key, but i would do it next way add primary key to owner data
public partial class OwnerData
{
public int Id { get; set; }
public string firstname { get; set; }
public string lastname { get; set; }
[Required]
[StringLength(60)]
[EmailAddress]
[MailNotExists] //this is a custom function
public string email { get; set; }
}
And modify your attribute
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var owner = validationContext.ObjectInstance as OwnerData;
if(owner == null) return new ValidationResult("Model is empty");
DbContext db = new DbContext();
var user = db.Users.FirstOrDefault(u => u.email == (string) value && u.id != owner.Id);
if (user == null)
return ValidationResult.Success;
else
return new ValidationResult("Mail already exists");
}