Validationattribute only when value is changed? - c#

I want to write a custom ValidationAttribute that checks if the given value is unique or not.
The problem is that in the edit screen, it is not guaranteed that the user actually changed the value, resulting in a false error.
Is there a way to check in my attribute whether the value actually changed? Or can I trigger the attribute only when the value has changed?
I'm getting the feeling this requirement maybe just doesn't belong in an attribute?

When you say ValidationAttibute, do you mean using DataAnnotations? If so, then all of this applies, else, sorry, I misunderstood and only part of this will.
I think your best bet is to do this in the repository or BLL using your unique key for the record, at least this is how I did it. Get the previous values of the record and see if they changed. If they did change, then run your uniqueness checks.
If you can get this logic into the ValidationAttribute, then more power to you, but I am not sure if a validationAttribute would be the best thing since there are ways to get around them. From my understanding of these attributes, you should use them as supplements only to business logic validations and not as the only way that you validate your model.
See here for more info on DataAnnotations
EDIT:
Fair enough, now let's see if I can give an answer to help you :) Check out this link, it is the code for uniqueness checking on any property in any table. Pretty in-depth LINQ to SQL stuff, but looks like it works well. You should be able to decorate any property with this just like using the <Required> or <StringLenght> attributes.
ASP.NET Forums

Related

nunit - set Order attribute from custom attribute of Test method

Let's say we have a custom attribute:
[Precondition(1, "Some precondition")]
This would implement [Test, Order(1), Description("Some precondition")]
Can I access and modify the Order attribute (or create one) for this method?
I can modify the Description and Author, but Order is not a possibility.
I have tried
1: context.Test.Properties["Order"][0] = order;
2:method.CustomAttributes.GetEnumerator()
by walking the stack frames with
Object[] attributes = method.GetCustomAttributes(typeof(PreconditionAttribute), false);
if (attributes.Length >= 1){...}
3:
OrderAttribute orderAttribute = (OrderAttribute)Attribute.GetCustomAttribute(i, typeof(OrderAttribute));
orderAttribute.Order = _order;
Which is readonly.
If I try orderAttribute.Order = new OrderAttribute(myOrd), it doesn't do anything.
I have two answers to choose from. One is in the vein of "Don't do this" and the other is about how to do it. Just for fun, I'm putting both answers up, separately, so they can compete with one another. This one is about why I don't think this is a good idea.
It's easy enough to write either
[Test, Order(1), Description("xxx")] or the equivalent...
[Test(Description="xxx"), Order(1)]
The proposed attribute gives users a second way to specify order, making it possible to assign two different orders to a test. Which of two attributes will win the day depends on (1) how each one is implemented, (2) the order in which the attributes are listed and (3) the platform on which you are running. For all practical purposes, it's non-deterministic.
Keeping the two things separate allows devs to decide which they need independently... which is why NUnit keeps them separate.
Using the standard attributes means that the devs can rely on the nunit documentation to tell them what the attributes do. If you implement your own attribute, you should document what it does in itself as well as what it does in the presence of the standard attributes... As stated above, that's difficult to predict.
I know this isn't a real answer in SO terms, but it's not pure opinion either. There are real technical issues in providing the kind of solution you want. I'd love to see what people think of it in comparison with "how to" I'm going to post next.
See my prior answer first! If you really want to do this, here's the how-to...
In order to combine the action of two existing attributes, you need equivalent code to those two attributes.
In this case both are extremely simple and both have about the same amount of code. DescriptionAttribute is based on PropertyAttribute so some of its code is hidden. OrderAttribute has a bit more logic because it checks to make sure the order has not already been set. Ultimately, both of them have code that implements the IApplyToTest interface.
Because they are both simple, I would copy the code, in order to avoid relying on implementation details that could change. Start with the slightly more complete OrderAttribute. Change its name. Modify the ApplyToTest method to set the description. You're done!
It will look something like this, depending on the names you use for properties...
public void ApplyToTest(Test test)
{
if (!test.Properties.ContainsKey(PropertyNames.Order))
test.Properties.Set(PropertyNames.Order, Order);
test.Properties.Set(PropertyNames.Description, Description);
}
A comment on what you tried...
There is no reason to think that creating an attribute in your code will do anything. NUnit has no way to know about those attributes. Your attribute cannot modify the code so that the test magically has other attributes. The only way Attributes communicate with NUnit is by having their interfaces (like IApplyToTest) called. And only attributes actually present in the code will receive such a call.

Model Validation in Web API

I have to validate three things when a consumer of my API tries to do an update on a customer.
Prevent the customer to be updated if:
The first name or last name are blank
For a certain country, if the customer's inner collection of X is empty, then throw an exception. X is hard to explain, so just assume it's some collection. For all other countries, X doesn't apply / will always be empty. But if it's a certain country, then X is required. So it's almost a conditional required attribute. A customer belongs to a country, so it's figured out from the JSON being sent.
Prevent the customer from being updated if some conditions in the database are true.
So basically i'm stuck with the following problem, and I wanted some advice on the most appropriately way to solve it:
Do I create an Action Filter to do the validation on the customer entity before the saving takes place? Or would it be better to create custom validation attribute derived from ValidationAttribute and override the IsValid member function.
Basically a question of saying
if (first name is empty, if x, if y, etc) vs (!ModelState.IsValid)
And then using IsValid to cause the custom attributes to work.
It seems like validation attributes are best for "simple" validation, i.e. required field. But once you start getting into things like "I need to look at my database, or analyze the http request header for custom values, and based on that, invalid = false" then it almost seems wrong to do this sort of stuff so close to the entity.
Thoughts?
Thanks!
I like FluentValidation a lot: https://github.com/JeremySkinner/FluentValidation
As you mentioned built-in validation attributes are limited. For complex validations you had better implement your own attributes or use a library like this.
One thing I like about FluentValidation is that it performs at model-level rather than field-level, meaning that you can use related fields' values for validation. For example
RuleFor(customer => customer.Discount).NotEqual(0).When(customer => customer.HasDiscount);
(Code excerpt taken from project's Wiki page)
It's also extensible so you can develop your own custom validators on top of this library as well.

Asp.net-MVC Custom Validation - NULL vs Empty

For security and consistency I would like to test on post-back if a field is missing? In Java (servlets in particular) when we perform a request.getParameter(key) the result is either a String value or otherwise NULL if the field is missing. In MVC I've created a custom validation that I call "ThrowOnNull". The behavior I'm trying to capture is: If an intended field is missing (null) I want to throw, otherwise return success.
Consider this Custom Validator (that doesn't work):
public class ThrowOnNull: ValidationAttribute
{
public ThrowOnNull() { }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null)
throw new Exception(validationContext.MemberName + " field expected, but missing."));
return ValidationResult.Success;
}
}
Is it possible to do what I want here? (this validator doesn't work as expected and it's because the framework is assigning NULL to an empty value [oh dear].)
UPDATE: Per #emodendroket, the following code example now works as expected:
public class ThrowOnMissing: ValidationAttribute
{
public ThrowOnMissing() { }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (!HttpContext.Current.Request.Form.AllKeys.Contains(validationContext.MemberName))
throw new Exception(validationContext.MemberName + " field expected, but missing.");
return ValidationResult.Success;
}
}
EDIT: I've cleaned up the question and example code significantly to make it all much clearer, hope it helps.
You're missing one important point - when you submit a form, all fields belonging to that form get submitted. If the user doesn't fill them, they're blank, so the validation for null can't really work like this...
You should probably redefine what "missing value" means.
EDIT:
After some discussion it seems you're not really concerned about null values, but about the security (false forms with missing fields and stuff like that). In that case you should simply use the standard - antiforgery token and maybe check the origin of the request. You should be more than fine this way. Checking for missing fields won't help a bit, because attacker can easily send those fields as well.
Over/Under posting is a real concern. Furthermore, if fields aren't submitted (due to a hack or DOS attack of some kind)
Not really.
Overposting is handled by the action method on the controller. It won't accept more parameters than you've specified.
Underposting will be handled pretty much the same way as if you didn't fill the text fields in the form, again a non-issue if you have validated your model correctly.
DDOS attack can't be prevented a believe me, some checking for missing fields won't help a bit if someone has a network powerful enough to cause DDOS. Just look up latest cases on attacks and you'll understand, that if HUGE servers can't withstand that, you certainly can't prevent it like this.
Your data validation shouldn't be too expensive either. It's a web, people don't like to wait too much.
If you want your own validator you can look at the dictionary HttpContext.Current.Request.Form. You can then do what you've proposed with this code:
if (!HttpContext.Current.Request.Form.AllKeys.Contains("prop"))
{
throw new Exception();
}
I think you're being unreasonably paranoid here about things. Note that I said Unreasonably paranoid, since a little paranoia is a good thing.
First, let us analyze the "threats" and determine the risks. You've presented several arguments:
Over-posting
Under-posting
Validation of empty vs null
The first item is not an issue in MVC if you are using a View Model. You simply can't post information that the view isn't expecting (well, you can, but it's ignored). If you're not using a view model (or not using a properly defined one) then you can use binding attributes to prevent posting of items you don't want bound.
The second, under-posting, if it's truly a concern for your model (99.999% of the time simply treating it as required is more than fine, but ok let's take your argument. The issue here is not the validation attribute, it's the model binder. You simply have to register a custom model binder that looks for missing values in your view model and throws if they are null. This is easily accomplished by reflecting over the bound model and comparing it to the posted values, then throw.
The problem with your RequiredThrowIfNull approach is.. what if it's not required and it's under posted? That's still an error according to your argument.
Finally, validation of empty vs null... you talk about expensive validation... I don't know what kind of expensive validation you could be talking about here, but server side there is noting in attributes that could be considered expensive.
The reason your attribute doesn't work is because validation attributes are called within a try/catch block by the framework already, and if it's null it's the very mechanism that treats it like empty (this mechanism also does things like catching parsing errors when a type is incorrect, such as characters in a datetime field.)
.NET is not Java, even though it largely works similarly... trying to re-implement Java patterns in .NET is going to lead to misery on your part because many basic philosophies are just different.
Even if we accept your argument of wanting to catch errors or be notified of hacking attempts, throwing is the wrong thing to do in most cases. Instead, just log the information from the model binder and continue on as normal. Only throw if you absolutely need to abort the request, and most of the time that simply isn't the case, even if you're being hacked... throwing will just cause the attacker to vary their attack until they no longer get the exception.
Frankly, this is an over-engineered, solution looking for a problem and there are many good .net specific ways of dealing with the REAL issues you are trying to solve.
#ignatandrei from ASP.NET answered the question:
by default MVC transforms empty string to null string.
http://forums.asp.net/t/2006146.aspx?Custom+Validation+and+NULL

How to solve cast issues in ValidationRule classes' properties?

I need to create a few tests for the user roles in a web application. To minimize the description, one of the tests involves checking if a menu entry is displayed or not for an user.
For this test, I use a table called UserRoles, that looks like this:
sUserName bDoesntHaveMenuX
User1 1
User2 0
User3 1
bDoesntHaveMenuX is of type bit.
I have a class derived from ValidationRule that checks if a certain text is present in a page, based on a XPath expression to locate the node where to look for the text.
The public properties of this class are:
string XPathExpression
string Text
bool FailIfFound
The last one dictates if the rule should fail if the text is found or not found.
In the test I added a datasource for the table mentioned in the beginning, called DS.
For the request I'm interested in I added a new instance of my validation rule class, with the following values:
Text=MenuX
XPathExpression=//div[#id='menu']//td
FailIfFound={{DS.UserRoles.bDoesntHaveMenuX}}
Unfortunately, this doesn't work.
The reason seems to be that the data binding process creates a context variable
DS.UserRoles.bDoesntHaveMenuX has the value "False" or "True". The value is a string, so the binding results in a casting error.
My options, as far as I can think of, are:
Change the validation rule to accept strings for FailIfFound. Not a valid
option, for 2 reasons: it's a hack and the same rule is used in
other places.
Make a new validation rule that will use the above mentioned one,
and implement the FailIfFound as string. I also don't like this, for
the same reason as above. It's a hack.
Make the test coded and do the proper cast before passing the data
to the validation rule. I don't like this one because I prefer to
have the test as coded only if there is no other way.
Which brings me to the question. Is there another way?
Thank you.
So the fundamental issue is that you have no control over how the data-binding treats the 'bit' data type, and it's getting converted to string instead of bool.
The only solution I can think of (which is sadly still a bit of a hack, but not so egregious as changing FailIfFound to string) is to create a WebTestPlugin, and in the PreRequestDataBinding or PreRequest event, convert the value from string to bool. Don't forget to add the plugin to your test(s) (easy mistake I have made).
Then when the validation rule is created it should pick up the nice new bool value and work correctly.
e.g.
string val = e.WebTest.Context["DS.UserRoles.bDoesntHaveMenuX"].ToString();
e.WebTest.Context["DS.UserRoles.bDoesntHaveMenuX"] = (val == "True");
I didn't actually try this... hope it works.
EDIT: round two... a better solution
Change the FailIfFound property to string (in a subclass as you mentioned), so it can work properly with data-binding.
Implement a TypeConverter that provides a dropdown list of valid values for the property in the rule's PropertyGrid (True, False), so in the GUI it looks identical to the rule having FailIfFound as a bool. You can still type your own value into the box when necessary (e.g. for data-binding).
Add the path of the .dll containing the TypeConverter code to your test project's References section.
This is what I have started doing and it is much more satisfying than having to type 'True' or 'False' in the property's edit box.

Adding and Removing Data Annotations from code

Is there away to add and remove DataAnnotations, in particular the [requried], from the code side of things? My problem is that I want to give the user the ability to save an incomplete form in our CRUD applications but at the same time use the power of the DataAnnotations validation.
If this is not possible, what is the best way I can go about this?
You can keep the DataAnnotation attributes on your model and then just manually clear the validation errors as needed from code. It might look something like this:
if (certainCondition == true) {
ModelState["someKey"].Errors.Clear();
ModelState["anotherKey"].Errors.Clear();
}
It is impossible to add, remove or modify DataAnnotations dynamically since they are Attributes. Attributes are part of the type and can't be changed during runtime.
You could use ModelState as Larsenal suggested provided that:
you use it After validation has executed. (prior to that, ModelState will be empty. It doesn't provide access to all validators, it only stores validator-errors after they've occurred)
you don't have any clientside validation that's based on the DataAnnotationValidators and fires errors that prevent you from even reaching the serverside validation.

Categories

Resources