Please ask if you can't understand what I'm asking.
I have created a custom ValidateAttribute for my ViewModel
i created it for validate properties which depend from another property of ViewModel
if (user checked "01" or "09" from QrupList) Then
Company name is needed
Name,surname and LastName are not needed
else
Company name is not needed
Name,surname and LastName are needed
I have ViewModel as below
[ValidateForGroupAttribute("Group", "CompanyName")]
public partial class AbonentViewModel
{
[DisplayName("Şirkət")]
public string CompanyName { get; set; }
[DisplayName("Soyadı")]
[Required(ErrorMessage = "Soyadı vacibdir")]
public string Surname { get; set; }
[DisplayName("Qrup")]
[Required(ErrorMessage = "Qrup vacibdir")]
public string Group{ get; set; }
public SelectList GroupList { get; set; }
}
My custom ValidationAttribute classes:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public sealed class ValidateForGroupAttribute : ValidationAttribute
{
private const string _defaultErrorMessage = "'{0}' a müvafiq '{1}' daxil din";
public ValidateForGroupAttribute(string originalProperty, string confirmPropertyCompany)
: base(_defaultErrorMessage)
{
OriginalProperty = originalProperty;
ConfirmPropertyCompany = confirmPropertyCompany;
}
public string OriginalProperty { get; private set; }
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
OriginalProperty, ConfirmPropertyCompany);
}
public override bool IsValid(object value)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
object originalValue = properties.Find(OriginalProperty, true).GetValue(value);
object confirmValueCompany = properties.Find(ConfirmPropertyCompany, true).GetValue(value);
if ((string)originalValue == "01" || (string)originalValue == "09")
return false;
else
return true;
}
}
How do I do it? What is wrong in my ValidationAttributes?
We looked at validation using data annotations a few months back, and decided it was better to use something like fluent validation, as we had complex business rules and logic that would have taken too much effort to realise with data annotations. Have a look at the documentation, and you will see fluent validation makes things like this easy.
Sorry, I did not get back sooner: Check fluent validation here
Your rule could look something like. Syntax not tested, but I am sure you will be able to figure it out.
public class AbonentViewModelValidator : AbstractValidator<AbonentViewModel> {
public AbonentViewModelValidator() {
RuleFor(model => model.CompanyName).NotEmpty().When(model => (model.GroupList.Id == 1 || model.GroupList.Id == 9 ));
RuleFor(model => model.Surname).NotEmpty().When(model => (model.GroupList.Id != 1 || model.GroupList.Id != 9 ));
RuleFor(model => model.Name).NotEmpty().When(model => (model.GroupList.Id != 1 || model.GroupList.Id != 9 ));
}
}
Related
I am using a unit of work to retrieve records / record form database, i am trying to implement some kind of a null object design pattern so that i dont have to check every-time if the returned object is null or not. I have tried searching online however i have not land on any good explanation on how to best achieve this in this current situation, I am familiar with the traditional approach for Null Object Design Pattern where you create a copy null class with hard coded properties and methods and return either the class or null-class based on outcome of the search in Db. however I feel that with the unit of work and repository patterns this approach is not valid. here is the class.
public class HR_Setup_Location
{
public string Street1 { get; set; }
public string Street2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
public string FullAddress{
get { return $"{Street1} {City} {Country}"; }
}
[ForeignKey("Setup")]
public int SetupId { get; set; }
public virtual Setup Setup { get; set; }
public virtual ICollection<HR_Setup_OfficeEvent> HR_Setup_OfficeEvents { get; set; }
}
I tried the following , which is doing the job for now, however i appreciate your feedback on the approach if you have tried something similar in a similar situation. and what is the best way to address null objects in this pattern.
public interface ISetupLocationRepository : IRepository<HR_Setup_Location>
{
HR_Setup_Location GetById(int LocationId);
}
public class SetupLocationRepository : Repository<HR_Setup_Location>, ISetupLocationRepository
{
private readonly DataBaseContext context;
public SetupLocationRepository(DataBaseContext context)
: base(context)
{
this.context = context;
}
public HR_Setup_Location GetById(int LocationId)
{
HR_Setup_Location Obj = context.HR_Setup_Locations.Where(p => p.HR_Setup_LocationId == LocationId).FirstOrDefault();
if (Obj != null)
{
return Obj;
}
else
{
HR_Setup_Location Obj2 = new HR_Setup_Location()
{
HR_Setup_LocationId = -1,
Street1 = string.Empty,
Street2 = string.Empty,
City = string.Empty,
State = string.Empty,
Country = string.Empty,
SetupId = -1,
};
Obj2.HR_Setup_OfficeEvents = null;
return Obj2;
}
}
}
Then with the unit of work I am trying to access the location address by calling:
string LocationName = Vacancy.HR_Setup_LocationId.HasValue ? unitOfWork.SetupLocations.GetById(Vacancy.HR_Setup_LocationId.Value).FullAddress : "";
so basically if no id is based it will return an empty string, and if an id is passed but the record is no longer available in DataBase then the null object return empty for Fulladdress
I want create model that will validate required field in model that depend on other field condition.
public class FixedDeposit
{
public int DebitAmount { get; set; }
public string PAN { get; set; }
}
Now if the DebitAmount is greater than 50,000 then PAN field is must be required.
You can implement IValidatableObject
public class FixedDeposit : IValidatableObject
{
public int DebitAmount { get; set; }
public string PAN { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (DebitAmount > 50000 && string.IsNullOrEmpty(PAN))
{
yield return new ValidationResult("PAN required for debits > 50,000.", new [] { "PAN" } );
}
}
}
http://weblogs.asp.net/scottgu/class-level-model-validation-with-ef-code-first-and-asp-net-mvc-3
You can also use MVC Foolproof validation package. This package provides you with many conditional validations in the form of annotations.
Complete list is here:
http://foolproof.codeplex.com/
You can add this library to your VS project as a package:
And, for your FixedPayment class, it should look something like this:
using Foolproof;
public class FixedDeposit
{
public int DebitAmount { get; set; }
[RequiredIf("DebitAmount", Operator.GreaterThan, 50000)]
public string PAN { get; set; }
}
Alternate code
using Foolproof;
public class FixedDeposit
{
public int DebitAmount { get; set; }
private bool _panRequired { get { return DebitAmount > 50000; } }
[RequiredIf("_panRequired", true, ErrorMessage="PAN is required if Debit Amount is greater than 50000")]
public string PAN { get; set; }
}
There are two options which you can use.
The first is the very easy to use and quite concise ExpressiveAnnotations JS library developed by Jaroslaw Waliszko. Follow this link to https://github.com/jwaliszko/ExpressiveAnnotations for more information. This library allows you to perform different conditional validations.
Similarly to Foolproof it is added to your Visual Studio environment through adding the NuGet package. Once added, within your model add the using statement using ExpressiveAnnotations.Attributes; Then simply use the RequiredIf declaration to do what you need. For example:
public class FixedDeposit
{
public int DebitAmount { get; set; }
[RequiredIf("DebitAmount >= 50000")]
public string PAN { get; set; }
}
The second option is to use ModelState.AddModelError(). This is done within your controller. Simply create a new method:
private void ValidateRequiredFields(modelname)
{
if(modelname.DebitAmount >= 50000)
{
if(modelname.PAN == null)
{
ModelState.AddModelError("PAN", "Place whatever error message you want here");
}
}
}
Next you place a reference to your validation method in whichever view method you want this to be called. The line to reference is ValidateRequiredFields(ModelName);
public class RequiredIfAttribute : RequiredAttribute
{
private String PropertyName { get; set; }
private Object DesiredValue { get; set; }
public RequiredIfAttribute(String propertyName, Object desiredvalue)
{
PropertyName = propertyName;
DesiredValue = desiredvalue;
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
Object instance = context.ObjectInstance;
Type type = instance.GetType();
Object proprtyvalue = type.GetProperty(PropertyName).GetValue(instance, null);
if (proprtyvalue.ToString() == DesiredValue.ToString())
{
ValidationResult result = base.IsValid(value, context);
return result;
}
return ValidationResult.Success;
}
}
Usage
[RequiredIf("DebitAmount",50000, ErrorMessage = "PAN field is required")]
public string PAN
{get;set;
}
I've encountered a problem working with Umbraco 6.1.5 and uSiteBuilder 3.0.0 where when I instantiate a strongly typed DocumentType using ContentHelper all the fields defined in the DocumentType are loaded into the object but fields like Name, Id or Children aren't loaded (they're null, empty or 0).
From what I can tell it's because the ContentHelper method responsible for instantiation is calling the empty constructor for DynamicNode. Is there something I'm missing? Should I be defining constructors on my Document Types?
Here's my DocumentType:
namespace USiteBuilderTest.DocumentTypes
{
[DocumentType]
public class Home : DocumentTypeBase
{
[DocumentTypeProperty(Name = "Field 1")]
public string Field1 { set; get; }
[DocumentTypeProperty(Name = "Field 2")]
public string Field2 { set; get; }
[DocumentTypeProperty(Name = "Field 3")]
public string Field3 { set; get; }
}
}
In-case it's helpful, here's the part of the code that's calling the empty constructor:
Type typeDocType = DocumentTypeManager.GetDocumentTypeType(node.NodeTypeAlias);
if (typeDocType != null)
{
ConstructorInfo constructorInfo = typeDocType.GetConstructor(new[] { typeof(int) });
if (constructorInfo == null)
{
retVal = (DocumentTypeBase)Activator.CreateInstance(typeDocType);
}
else
{
retVal = (DocumentTypeBase)constructorInfo.Invoke(new object[] { node.Id });
}
It seems that despite the Codeplex project description clearly stating that branch "3.0.0" should be used with Umbraco v6 there's actually a specific v6 branch that should be used instead (labelled "version6API").
Credit for the answer goes to Vladan Ostojic who answered the question on the Umbraco forums:
http://our.umbraco.org/projects/developer-tools/usitebuilder/usitebuilder-support/44774-uSiteBuilder-30-loads-DocumentType-fields-but-not-the-DynamicNode-fields?p=0#comment161011
I also experienced this problem. A solution could be to add an empty constructor and a constructor that has nodeid as input. Like this:
namespace USiteBuilderTest.DocumentTypes
{
[DocumentType]
public class Home : DocumentTypeBase
{
public Home() {}
public Home(int nodeId) : base(nodeId) { }
[DocumentTypeProperty(Name = "Field 1")]
public string Field1 { set; get; }
[DocumentTypeProperty(Name = "Field 2")]
public string Field2 { set; get; }
[DocumentTypeProperty(Name = "Field 3")]
public string Field3 { set; get; }
}
}
That did the trick for me.
I am trying to use a combination of Domain Driven Design with Test Driven Development for this application I am building in ASP.NET MVC 3. My archictecture is set up with Repositories, Domain Models, View Models, Controllers and Views. All validation will be handled in the view model. I set up my view model to inherit from "IValidatableObject" so that my validation attributes and my custom validation that i set up in the "Validate" method are both executed when my controller method calls "ModelState.IsValid". The problem I am running into is accessing my repository in the Validate method of my view model. I need to access the repository to check for duplicate records in the database. It seems like the best idea would be to create a property of IRepository type and set that property by passing injecting my repository into the constructor of the view model. For example:
public class UserViewModel : IValidatableObject
{
public UserViewModel(User user, IUserRepository userRepository)
{
FirstName = user.FirstName;
LastName = user.LastName;
UserRepository = userRepository;
UserName = user.UserName;
}
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public IUserRepository UserRepository { get; set; }
public IEnumerable<ValidationResult> Validate()
{
UserCriteria criteria = new UserCriteria { UserName = this.UserName };
IList<User> users = UserRepository.SearchUsers(criteria);
if (users != null && users.count() > 0)
{
yield return new ValidationResult("User with username " + this.UserName + " already exists."
}
}
}
Do you guys think this is a good idea?
It is good enough but if I were you, I would use
...
private readonly Func<IUserRepository> userRepositoryFactory;
...
public IEnumerable<ValidationResult> Validate()
{
UserCriteria criteria = new UserCriteria { UserName = this.UserName };
using(var UserRepository = userRepositoryFactory())
{
IList<User> users = UserRepository.SearchUsers(criteria);
if (users != null && users.count() > 0)
{
yield return new ValidationResult("User with username " + this.UserName + " already exists."
}
}
}
You can add Domain Service class to get object match with your criteria and validated at domain service level
public class PurchaseOrder
{
public string Id { get; private set; }
public string PONumber { get; private set; }
public string Description { get; private set; }
public decimal Total { get; private set; }
public DateTime SubmissionDate { get; private set; }
public ICollection<Invoice> Invoices { get; private set; }
public decimal InvoiceTotal
{
get { return this.Invoices.Select(x => x.Amount).Sum(); }
}
}
public class PurchaseOrderService
{
public PurchaseOrderService(IPurchaseOrderRepository repository)
{
this.repository = repository;
}
readonly IPurchaseOrderRepository repository;
public void CheckPurchasedOrderExsist(string purchaseOrderId)
{
var purchaseOrder = this.repository.Get(purchaseOrderId);
if (purchaseOrder != null)
throw new Exception("PO already exist!");
}
}
I have a search form where I can search on one of two fields. Only one of the two is required. Is there a way to cleanly do this in the MVC provided validation?
If this is server validation, you could create a Custom Validation Attribute for this:
[ConditionalRequired("Field1","Field2")]
public class MyModel()
{
public string Field1 { get; set; }
public string Field2 { get; set; }
}
Then you can create a custom validation attribute.. something like the following:
[AttributeUsage( AttributeTargets.Class, AllowMultiple = true, Inherited = true )]
public sealed class ConditionalRequiredAttribute : ValidationAttribute
{
private const string _defaultErrorMessage = "Either '{0}' or '{1}' must be provided.";
public string FirstProperty { get; private set; }
public string SecondProperty { get; private set; }
public ConditionalRequiredAttribute( string first, string second )
: base( _defaultErrorMessage )
{
FirstProperty = first;
SecondProperty = second;
}
public override string FormatErrorMessage( string name )
{
return String.Format( CultureInfo.CurrentUICulture, ErrorMessageString,
FirstProperty, SecondProperty );
}
public override bool IsValid( object value )
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties( value );
string originalValue = properties.Find( FirstProperty, true ).GetValue( value ).ToString(); // ToString() MAY through a null reference exception.. i haven't tested it at all
string confirmValue = properties.Find( SecondProperty, true ).GetValue( value ).ToString( );
return !String.IsNullOrWhiteSpace( originalValue ) || !String.IsNullOrWhiteSpace( confirmValue ); // ensure at least one of these values isn't null or isn't whitespace
}
}
Its a little verbose, but it gives you the flexibility to able this attribute directly to your model as opposed to applying it to each individual field