Latest commit

History

41 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SimpleValidator

A simple .Net validation library written in C#

It's based largely on the FluentValidation libary with some changes in it's usage.

Why SimpleValidator?

I needed a validation library that satisfied the following criteria:

  • Quick and simple validation checks
  • Complex business rule validation
  • Multiple stages of validation (i.e. do the simple stuff first, then more complex validation, but don't bother with the complex stuff if the simple stuff fails)
  • Avoid attribute based validation (I try to avoid using attributes. People think, I've validated my MVC model so my service method should be good to go. Not the case. Service methods should always validate everything as you cannot guarantee validation has been performed)
  • Readable
  • Central

What I came up with was the following...

As there is quite a bit to cover I'm going to start simply, and add complexity.

Structure

There is 2 types of validation you can perform using SimpleValidator. Extension methods and Validator. The validator essentially uses the extension methods behind the scenes but provides a useful wrapper for doing your validations and groups the error messages in a meaningful way.

Extension methods

As a start, there are some simple extension method you can use.

Include the following using statement:

using SimpleValidator.Extensions;

Then you should have some useful helper methods as shown below:

string email = "test@email.com";
if (email.IsLength(5)) ...
if (email.IsEmail()) ...
if (email.IsMatch("test2@email.com")) ...
if (email.IsInt()) ...
if (email.IsNumber()) ...

The full list of extension methods is as follows:

// string based extension methods (IsNotNullOrEmpty and IsNotNullOrWhiteSpace are the only methods that have inverse tests. The assumption is to always assume the positive.
[string].IsNotNullOrEmpty
[string].IsNullOrEmpty
[string].IsNotNullOrWhiteSpace
[string].IsNullOrWhiteSpace();
[string].IsBetweenLength
[string].IsMaxLength
[string].IsMinLength
[string].IsExactLength
[string].IsEmail
[string].IsPassword
[string].IsCreditCard
[string].IsEqualTo
// object based extension methods
[object].IsNotNull
[object].IsNull
[object].Is
[object].IsNot
[object].IsRule // validating against an interface for more complex business rules
[object].IsInt
[object].IsShort
[object].IsLong
[object].IsDouble
[object].IsBool
[object].IsNumber
[object].Is<T>
// integer (same dor double, decimal, short and long)
[integer].IsNotZero
[integer].IsEqualTo
[integer].IsGreaterThan
[integer].IsLessThan
[integer].IsBetween
// datetime
[datetime].IsDate
[datetime].IsGreaterThan
[datetime].IsGreaterThanOrEqualTo
[datetime].IsLessThan
[datetime].IsLessThanOrEqualTo
[datetime].IsEqualTo
[datetime].IsBetween
[datetime].IsGreaterThanDateOnly
[datetime].IsGreaterThanOrEqualToDateOnly
[datetime].IsLessThanDateOnly
[datetime].IsLessThanOrEqualToDateOnly
[datetime].IsEqualToDateOnly
[datetime].IsBetweenDateOnly

From here you also have access to some helpful conversion methods, e.g.

string input = "5";
if (input.IsInt())
return input.ToInt();

A full list of these are as follows

.ToInt()
.ToShort()
.ToLong()
.ToDouble()
.ToDecimal()
.ToBool()
// and then a more generic one
.To<T>()

Validator

Extension methods work great for quick simple checks, but you probably want to do more complex validation.

For this, lets create a Validator object. From there you can use it to validate several properties of an object with a few overrides as shown below

Remember to include using SimpleValidator;

For each text method you have the following overrides:

SimpleValidator.Validator validator = new Validator();
// value
validator.IsEmail("test@email.com");
// name, value
validator.IsEmail("Email", "test@email.com");
// name, value, message
validator.IsEmail("Email", "test@email.com", "Please enter a valid email address");
if (validator.IsValid())
{
// everything is good
}
else
{
// validator.Errors contains a name / message dictionary of your errors }

It supports chaining so you could write it as follows

validator
.IsEmail("test@email.com")
.IsEmail("Email", "test@email.com", "This is an error message")
.IsEmail("Email", "test@email.com").WithMessage("This is an error message")

This is quite usefull for more complex object validation e.g.

// validate the name
validator
.NotNull(name).WithMessage("Name cannot be an empty string")
.IsLength(name, 5).WithMessage("Name must be at least 5 characters long")
.IsLength(name, 5, 20).WithMessage("Name must between 5 and 20 characters")
.Must(() =>
{
return name.Contains("abc");
}).WithMessage("Name cannot contain abc")
.MustNot(() =>
{
return name.Contains("bob");
}).WithMessage("Name cannot contain bob");

Each of the messages are added to an Error collection within the validator object. It also has a IsValid property. The validation is done as the method is called, which means you can stagger the validation if needed e.g.

string name = "Gordon";
// validate the name
validator
.NotNull(name).WithMessage("Name cannot be an empty string");
if (validator.IsValid)
{
// do something here that is dependant on name not being null
validator.Must(() =>
{
// some other condition
return true;
});
if (validator.IsValid)
{
// and down the rabit hole we go...
}
}

IRule

For more complex validation you can use the Is and IsNot validation methods passing in a function which should return a bool. For more serious business rule validations, you can pass in any object implementing the IRule interface to the IsRule(...) method. All this combined gives you some flexibility on how you go about implementing your validation.

Point is, every service method should implement validation for every incoming model.

Ok, I have validation errors, now what?

Implementation

Let's say we have a method

private User AddUser(UserModel user)
{
}

You pass in a UserModel, and if all goes well, the user is added and it returns a User object.

What happens if we encounter validation errors? Once approach is to throw an exception (Not ideal but works) and the library has an exception ready for this.

ValidationException


try
{
// ...
ServiceManager.AddUser(model);
// ...
}
catch (ValidationException ex)
{
// validation error - The validator is passed in as part of the exception so you can access the errors ex.Validator.Errors
}
catch (Exception ex)
{
// something else... woops
}

And in your validation method.

private User AddUser(UserModel user)
{
Validator validator = new Validator();
// validation happens here...
if (!validator.IsValid)
{
validator.ThrowValidationException();
}
// or you can do this without the IsValid check (it checks it internally)
validator.ThrowValidationExceptionIfInvalid();
}

ValidationMethodResult

Another approach is to use the ValidationMethodResult. Changing the above code it now looks as follows

public ValidationMethodResult<User> AddUser(UserModel model)
{
Validator validator = new Validator();
// do validation
return new ValidationMethodResult<User>(validator, user);
}

And then in your calling method

try
{
// ...
var result = ServiceManager.AddUser(model);
if (result.IsValid())
{
// all good and you have access to the return user
return Content(result.Result.Name); // The return users name...
}
else
{
// show errors using result.Errors
}
// ...
}
catch (Exception ex)
{
// something else... woops
}

It's a cleaner approach, allows you to specify complex return types and provides a boolean indication reflecting the validation state of the current object. You could use simple out param's and use a boolean return type for all methods, but I'm not a fan (That doesn't mean it's not right)

About

A simple .Net validation library written in C#

Resources

Stars

36 stars

Watchers

6 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

41 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SimpleValidator

A simple .Net validation library written in C#

It's based largely on the FluentValidation libary with some changes in it's usage.

Why SimpleValidator?

I needed a validation library that satisfied the following criteria:

  • Quick and simple validation checks
  • Complex business rule validation
  • Multiple stages of validation (i.e. do the simple stuff first, then more complex validation, but don't bother with the complex stuff if the simple stuff fails)
  • Avoid attribute based validation (I try to avoid using attributes. People think, I've validated my MVC model so my service method should be good to go. Not the case. Service methods should always validate everything as you cannot guarantee validation has been performed)
  • Readable
  • Central

What I came up with was the following...

As there is quite a bit to cover I'm going to start simply, and add complexity.

Structure

There is 2 types of validation you can perform using SimpleValidator. Extension methods and Validator. The validator essentially uses the extension methods behind the scenes but provides a useful wrapper for doing your validations and groups the error messages in a meaningful way.

Extension methods

As a start, there are some simple extension method you can use.

Include the following using statement:

using SimpleValidator.Extensions;

Then you should have some useful helper methods as shown below:

string email = "test@email.com";
if (email.IsLength(5)) ...
if (email.IsEmail()) ...
if (email.IsMatch("test2@email.com")) ...
if (email.IsInt()) ...
if (email.IsNumber()) ...

The full list of extension methods is as follows:

// string based extension methods (IsNotNullOrEmpty and IsNotNullOrWhiteSpace are the only methods that have inverse tests. The assumption is to always assume the positive.
[string].IsNotNullOrEmpty
[string].IsNullOrEmpty
[string].IsNotNullOrWhiteSpace
[string].IsNullOrWhiteSpace();
[string].IsBetweenLength
[string].IsMaxLength
[string].IsMinLength
[string].IsExactLength
[string].IsEmail
[string].IsPassword
[string].IsCreditCard
[string].IsEqualTo
// object based extension methods
[object].IsNotNull
[object].IsNull
[object].Is
[object].IsNot
[object].IsRule // validating against an interface for more complex business rules
[object].IsInt
[object].IsShort
[object].IsLong
[object].IsDouble
[object].IsBool
[object].IsNumber
[object].Is<T>
// integer (same dor double, decimal, short and long)
[integer].IsNotZero
[integer].IsEqualTo
[integer].IsGreaterThan
[integer].IsLessThan
[integer].IsBetween
// datetime
[datetime].IsDate
[datetime].IsGreaterThan
[datetime].IsGreaterThanOrEqualTo
[datetime].IsLessThan
[datetime].IsLessThanOrEqualTo
[datetime].IsEqualTo
[datetime].IsBetween
[datetime].IsGreaterThanDateOnly
[datetime].IsGreaterThanOrEqualToDateOnly
[datetime].IsLessThanDateOnly
[datetime].IsLessThanOrEqualToDateOnly
[datetime].IsEqualToDateOnly
[datetime].IsBetweenDateOnly

From here you also have access to some helpful conversion methods, e.g.

string input = "5";
if (input.IsInt())
return input.ToInt();

A full list of these are as follows

.ToInt()
.ToShort()
.ToLong()
.ToDouble()
.ToDecimal()
.ToBool()
// and then a more generic one
.To<T>()

Validator

Extension methods work great for quick simple checks, but you probably want to do more complex validation.

For this, lets create a Validator object. From there you can use it to validate several properties of an object with a few overrides as shown below

Remember to include using SimpleValidator;

For each text method you have the following overrides:

SimpleValidator.Validator validator = new Validator();
// value
validator.IsEmail("test@email.com");
// name, value
validator.IsEmail("Email", "test@email.com");
// name, value, message
validator.IsEmail("Email", "test@email.com", "Please enter a valid email address");
if (validator.IsValid())
{
// everything is good
}
else
{
// validator.Errors contains a name / message dictionary of your errors }

It supports chaining so you could write it as follows

validator
.IsEmail("test@email.com")
.IsEmail("Email", "test@email.com", "This is an error message")
.IsEmail("Email", "test@email.com").WithMessage("This is an error message")

This is quite usefull for more complex object validation e.g.

// validate the name
validator
.NotNull(name).WithMessage("Name cannot be an empty string")
.IsLength(name, 5).WithMessage("Name must be at least 5 characters long")
.IsLength(name, 5, 20).WithMessage("Name must between 5 and 20 characters")
.Must(() =>
{
return name.Contains("abc");
}).WithMessage("Name cannot contain abc")
.MustNot(() =>
{
return name.Contains("bob");
}).WithMessage("Name cannot contain bob");

Each of the messages are added to an Error collection within the validator object. It also has a IsValid property. The validation is done as the method is called, which means you can stagger the validation if needed e.g.

string name = "Gordon";
// validate the name
validator
.NotNull(name).WithMessage("Name cannot be an empty string");
if (validator.IsValid)
{
// do something here that is dependant on name not being null
validator.Must(() =>
{
// some other condition
return true;
});
if (validator.IsValid)
{
// and down the rabit hole we go...
}
}

IRule

For more complex validation you can use the Is and IsNot validation methods passing in a function which should return a bool. For more serious business rule validations, you can pass in any object implementing the IRule interface to the IsRule(...) method. All this combined gives you some flexibility on how you go about implementing your validation.

Point is, every service method should implement validation for every incoming model.

Ok, I have validation errors, now what?

Implementation

Let's say we have a method

private User AddUser(UserModel user)
{
}

You pass in a UserModel, and if all goes well, the user is added and it returns a User object.

What happens if we encounter validation errors? Once approach is to throw an exception (Not ideal but works) and the library has an exception ready for this.

ValidationException


try
{
// ...
ServiceManager.AddUser(model);
// ...
}
catch (ValidationException ex)
{
// validation error - The validator is passed in as part of the exception so you can access the errors ex.Validator.Errors
}
catch (Exception ex)
{
// something else... woops
}

And in your validation method.

private User AddUser(UserModel user)
{
Validator validator = new Validator();
// validation happens here...
if (!validator.IsValid)
{
validator.ThrowValidationException();
}
// or you can do this without the IsValid check (it checks it internally)
validator.ThrowValidationExceptionIfInvalid();
}

ValidationMethodResult

Another approach is to use the ValidationMethodResult. Changing the above code it now looks as follows

public ValidationMethodResult<User> AddUser(UserModel model)
{
Validator validator = new Validator();
// do validation
return new ValidationMethodResult<User>(validator, user);
}

And then in your calling method

try
{
// ...
var result = ServiceManager.AddUser(model);
if (result.IsValid())
{
// all good and you have access to the return user
return Content(result.Result.Name); // The return users name...
}
else
{
// show errors using result.Errors
}
// ...
}
catch (Exception ex)
{
// something else... woops
}

It's a cleaner approach, allows you to specify complex return types and provides a boolean indication reflecting the validation state of the current object. You could use simple out param's and use a boolean return type for all methods, but I'm not a fan (That doesn't mean it's not right)

About

A simple .Net validation library written in C#

Resources

Stars

36 stars

Watchers

6 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

41 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SimpleValidator

A simple .Net validation library written in C#

It's based largely on the FluentValidation libary with some changes in it's usage.

Why SimpleValidator?

I needed a validation library that satisfied the following criteria:

  • Quick and simple validation checks
  • Complex business rule validation
  • Multiple stages of validation (i.e. do the simple stuff first, then more complex validation, but don't bother with the complex stuff if the simple stuff fails)
  • Avoid attribute based validation (I try to avoid using attributes. People think, I've validated my MVC model so my service method should be good to go. Not the case. Service methods should always validate everything as you cannot guarantee validation has been performed)
  • Readable
  • Central

What I came up with was the following...

As there is quite a bit to cover I'm going to start simply, and add complexity.

Structure

There is 2 types of validation you can perform using SimpleValidator. Extension methods and Validator. The validator essentially uses the extension methods behind the scenes but provides a useful wrapper for doing your validations and groups the error messages in a meaningful way.

Extension methods

As a start, there are some simple extension method you can use.

Include the following using statement:

using SimpleValidator.Extensions;

Then you should have some useful helper methods as shown below:

string email = "test@email.com";
if (email.IsLength(5)) ...
if (email.IsEmail()) ...
if (email.IsMatch("test2@email.com")) ...
if (email.IsInt()) ...
if (email.IsNumber()) ...

The full list of extension methods is as follows:

// string based extension methods (IsNotNullOrEmpty and IsNotNullOrWhiteSpace are the only methods that have inverse tests. The assumption is to always assume the positive.
[string].IsNotNullOrEmpty
[string].IsNullOrEmpty
[string].IsNotNullOrWhiteSpace
[string].IsNullOrWhiteSpace();
[string].IsBetweenLength
[string].IsMaxLength
[string].IsMinLength
[string].IsExactLength
[string].IsEmail
[string].IsPassword
[string].IsCreditCard
[string].IsEqualTo
// object based extension methods
[object].IsNotNull
[object].IsNull
[object].Is
[object].IsNot
[object].IsRule // validating against an interface for more complex business rules
[object].IsInt
[object].IsShort
[object].IsLong
[object].IsDouble
[object].IsBool
[object].IsNumber
[object].Is<T>
// integer (same dor double, decimal, short and long)
[integer].IsNotZero
[integer].IsEqualTo
[integer].IsGreaterThan
[integer].IsLessThan
[integer].IsBetween
// datetime
[datetime].IsDate
[datetime].IsGreaterThan
[datetime].IsGreaterThanOrEqualTo
[datetime].IsLessThan
[datetime].IsLessThanOrEqualTo
[datetime].IsEqualTo
[datetime].IsBetween
[datetime].IsGreaterThanDateOnly
[datetime].IsGreaterThanOrEqualToDateOnly
[datetime].IsLessThanDateOnly
[datetime].IsLessThanOrEqualToDateOnly
[datetime].IsEqualToDateOnly
[datetime].IsBetweenDateOnly

From here you also have access to some helpful conversion methods, e.g.

string input = "5";
if (input.IsInt())
return input.ToInt();

A full list of these are as follows

.ToInt()
.ToShort()
.ToLong()
.ToDouble()
.ToDecimal()
.ToBool()
// and then a more generic one
.To<T>()

Validator

Extension methods work great for quick simple checks, but you probably want to do more complex validation.

For this, lets create a Validator object. From there you can use it to validate several properties of an object with a few overrides as shown below

Remember to include using SimpleValidator;

For each text method you have the following overrides:

SimpleValidator.Validator validator = new Validator();
// value
validator.IsEmail("test@email.com");
// name, value
validator.IsEmail("Email", "test@email.com");
// name, value, message
validator.IsEmail("Email", "test@email.com", "Please enter a valid email address");
if (validator.IsValid())
{
// everything is good
}
else
{
// validator.Errors contains a name / message dictionary of your errors }

It supports chaining so you could write it as follows

validator
.IsEmail("test@email.com")
.IsEmail("Email", "test@email.com", "This is an error message")
.IsEmail("Email", "test@email.com").WithMessage("This is an error message")

This is quite usefull for more complex object validation e.g.

// validate the name
validator
.NotNull(name).WithMessage("Name cannot be an empty string")
.IsLength(name, 5).WithMessage("Name must be at least 5 characters long")
.IsLength(name, 5, 20).WithMessage("Name must between 5 and 20 characters")
.Must(() =>
{
return name.Contains("abc");
}).WithMessage("Name cannot contain abc")
.MustNot(() =>
{
return name.Contains("bob");
}).WithMessage("Name cannot contain bob");

Each of the messages are added to an Error collection within the validator object. It also has a IsValid property. The validation is done as the method is called, which means you can stagger the validation if needed e.g.

string name = "Gordon";
// validate the name
validator
.NotNull(name).WithMessage("Name cannot be an empty string");
if (validator.IsValid)
{
// do something here that is dependant on name not being null
validator.Must(() =>
{
// some other condition
return true;
});
if (validator.IsValid)
{
// and down the rabit hole we go...
}
}

IRule

For more complex validation you can use the Is and IsNot validation methods passing in a function which should return a bool. For more serious business rule validations, you can pass in any object implementing the IRule interface to the IsRule(...) method. All this combined gives you some flexibility on how you go about implementing your validation.

Point is, every service method should implement validation for every incoming model.

Ok, I have validation errors, now what?

Implementation

Let's say we have a method

private User AddUser(UserModel user)
{
}

You pass in a UserModel, and if all goes well, the user is added and it returns a User object.

What happens if we encounter validation errors? Once approach is to throw an exception (Not ideal but works) and the library has an exception ready for this.

ValidationException


try
{
// ...
ServiceManager.AddUser(model);
// ...
}
catch (ValidationException ex)
{
// validation error - The validator is passed in as part of the exception so you can access the errors ex.Validator.Errors
}
catch (Exception ex)
{
// something else... woops
}

And in your validation method.

private User AddUser(UserModel user)
{
Validator validator = new Validator();
// validation happens here...
if (!validator.IsValid)
{
validator.ThrowValidationException();
}
// or you can do this without the IsValid check (it checks it internally)
validator.ThrowValidationExceptionIfInvalid();
}

ValidationMethodResult

Another approach is to use the ValidationMethodResult. Changing the above code it now looks as follows

public ValidationMethodResult<User> AddUser(UserModel model)
{
Validator validator = new Validator();
// do validation
return new ValidationMethodResult<User>(validator, user);
}

And then in your calling method

try
{
// ...
var result = ServiceManager.AddUser(model);
if (result.IsValid())
{
// all good and you have access to the return user
return Content(result.Result.Name); // The return users name...
}
else
{
// show errors using result.Errors
}
// ...
}
catch (Exception ex)
{
// something else... woops
}

It's a cleaner approach, allows you to specify complex return types and provides a boolean indication reflecting the validation state of the current object. You could use simple out param's and use a boolean return type for all methods, but I'm not a fan (That doesn't mean it's not right)

About

A simple .Net validation library written in C#

Resources

Stars

36 stars

Watchers

6 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

41 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SimpleValidator

A simple .Net validation library written in C#

It's based largely on the FluentValidation libary with some changes in it's usage.

Why SimpleValidator?

I needed a validation library that satisfied the following criteria:

  • Quick and simple validation checks
  • Complex business rule validation
  • Multiple stages of validation (i.e. do the simple stuff first, then more complex validation, but don't bother with the complex stuff if the simple stuff fails)
  • Avoid attribute based validation (I try to avoid using attributes. People think, I've validated my MVC model so my service method should be good to go. Not the case. Service methods should always validate everything as you cannot guarantee validation has been performed)
  • Readable
  • Central

What I came up with was the following...

As there is quite a bit to cover I'm going to start simply, and add complexity.

Structure

There is 2 types of validation you can perform using SimpleValidator. Extension methods and Validator. The validator essentially uses the extension methods behind the scenes but provides a useful wrapper for doing your validations and groups the error messages in a meaningful way.

Extension methods

As a start, there are some simple extension method you can use.

Include the following using statement:

using SimpleValidator.Extensions;

Then you should have some useful helper methods as shown below:

string email = "test@email.com";
if (email.IsLength(5)) ...
if (email.IsEmail()) ...
if (email.IsMatch("test2@email.com")) ...
if (email.IsInt()) ...
if (email.IsNumber()) ...

The full list of extension methods is as follows:

// string based extension methods (IsNotNullOrEmpty and IsNotNullOrWhiteSpace are the only methods that have inverse tests. The assumption is to always assume the positive.
[string].IsNotNullOrEmpty
[string].IsNullOrEmpty
[string].IsNotNullOrWhiteSpace
[string].IsNullOrWhiteSpace();
[string].IsBetweenLength
[string].IsMaxLength
[string].IsMinLength
[string].IsExactLength
[string].IsEmail
[string].IsPassword
[string].IsCreditCard
[string].IsEqualTo
// object based extension methods
[object].IsNotNull
[object].IsNull
[object].Is
[object].IsNot
[object].IsRule // validating against an interface for more complex business rules
[object].IsInt
[object].IsShort
[object].IsLong
[object].IsDouble
[object].IsBool
[object].IsNumber
[object].Is<T>
// integer (same dor double, decimal, short and long)
[integer].IsNotZero
[integer].IsEqualTo
[integer].IsGreaterThan
[integer].IsLessThan
[integer].IsBetween
// datetime
[datetime].IsDate
[datetime].IsGreaterThan
[datetime].IsGreaterThanOrEqualTo
[datetime].IsLessThan
[datetime].IsLessThanOrEqualTo
[datetime].IsEqualTo
[datetime].IsBetween
[datetime].IsGreaterThanDateOnly
[datetime].IsGreaterThanOrEqualToDateOnly
[datetime].IsLessThanDateOnly
[datetime].IsLessThanOrEqualToDateOnly
[datetime].IsEqualToDateOnly
[datetime].IsBetweenDateOnly

From here you also have access to some helpful conversion methods, e.g.

string input = "5";
if (input.IsInt())
return input.ToInt();

A full list of these are as follows

.ToInt()
.ToShort()
.ToLong()
.ToDouble()
.ToDecimal()
.ToBool()
// and then a more generic one
.To<T>()

Validator

Extension methods work great for quick simple checks, but you probably want to do more complex validation.

For this, lets create a Validator object. From there you can use it to validate several properties of an object with a few overrides as shown below

Remember to include using SimpleValidator;

For each text method you have the following overrides:

SimpleValidator.Validator validator = new Validator();
// value
validator.IsEmail("test@email.com");
// name, value
validator.IsEmail("Email", "test@email.com");
// name, value, message
validator.IsEmail("Email", "test@email.com", "Please enter a valid email address");
if (validator.IsValid())
{
// everything is good
}
else
{
// validator.Errors contains a name / message dictionary of your errors }

It supports chaining so you could write it as follows

validator
.IsEmail("test@email.com")
.IsEmail("Email", "test@email.com", "This is an error message")
.IsEmail("Email", "test@email.com").WithMessage("This is an error message")

This is quite usefull for more complex object validation e.g.

// validate the name
validator
.NotNull(name).WithMessage("Name cannot be an empty string")
.IsLength(name, 5).WithMessage("Name must be at least 5 characters long")
.IsLength(name, 5, 20).WithMessage("Name must between 5 and 20 characters")
.Must(() =>
{
return name.Contains("abc");
}).WithMessage("Name cannot contain abc")
.MustNot(() =>
{
return name.Contains("bob");
}).WithMessage("Name cannot contain bob");

Each of the messages are added to an Error collection within the validator object. It also has a IsValid property. The validation is done as the method is called, which means you can stagger the validation if needed e.g.

string name = "Gordon";
// validate the name
validator
.NotNull(name).WithMessage("Name cannot be an empty string");
if (validator.IsValid)
{
// do something here that is dependant on name not being null
validator.Must(() =>
{
// some other condition
return true;
});
if (validator.IsValid)
{
// and down the rabit hole we go...
}
}

IRule

For more complex validation you can use the Is and IsNot validation methods passing in a function which should return a bool. For more serious business rule validations, you can pass in any object implementing the IRule interface to the IsRule(...) method. All this combined gives you some flexibility on how you go about implementing your validation.

Point is, every service method should implement validation for every incoming model.

Ok, I have validation errors, now what?

Implementation

Let's say we have a method

private User AddUser(UserModel user)
{
}

You pass in a UserModel, and if all goes well, the user is added and it returns a User object.

What happens if we encounter validation errors? Once approach is to throw an exception (Not ideal but works) and the library has an exception ready for this.

ValidationException


try
{
// ...
ServiceManager.AddUser(model);
// ...
}
catch (ValidationException ex)
{
// validation error - The validator is passed in as part of the exception so you can access the errors ex.Validator.Errors
}
catch (Exception ex)
{
// something else... woops
}

And in your validation method.

private User AddUser(UserModel user)
{
Validator validator = new Validator();
// validation happens here...
if (!validator.IsValid)
{
validator.ThrowValidationException();
}
// or you can do this without the IsValid check (it checks it internally)
validator.ThrowValidationExceptionIfInvalid();
}

ValidationMethodResult

Another approach is to use the ValidationMethodResult. Changing the above code it now looks as follows

public ValidationMethodResult<User> AddUser(UserModel model)
{
Validator validator = new Validator();
// do validation
return new ValidationMethodResult<User>(validator, user);
}

And then in your calling method

try
{
// ...
var result = ServiceManager.AddUser(model);
if (result.IsValid())
{
// all good and you have access to the return user
return Content(result.Result.Name); // The return users name...
}
else
{
// show errors using result.Errors
}
// ...
}
catch (Exception ex)
{
// something else... woops
}

It's a cleaner approach, allows you to specify complex return types and provides a boolean indication reflecting the validation state of the current object. You could use simple out param's and use a boolean return type for all methods, but I'm not a fan (That doesn't mean it's not right)

About

A simple .Net validation library written in C#

Resources

Stars

36 stars

Watchers

6 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

41 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SimpleValidator

A simple .Net validation library written in C#

It's based largely on the FluentValidation libary with some changes in it's usage.

Why SimpleValidator?

I needed a validation library that satisfied the following criteria:

  • Quick and simple validation checks
  • Complex business rule validation
  • Multiple stages of validation (i.e. do the simple stuff first, then more complex validation, but don't bother with the complex stuff if the simple stuff fails)
  • Avoid attribute based validation (I try to avoid using attributes. People think, I've validated my MVC model so my service method should be good to go. Not the case. Service methods should always validate everything as you cannot guarantee validation has been performed)
  • Readable
  • Central

What I came up with was the following...

As there is quite a bit to cover I'm going to start simply, and add complexity.

Structure

There is 2 types of validation you can perform using SimpleValidator. Extension methods and Validator. The validator essentially uses the extension methods behind the scenes but provides a useful wrapper for doing your validations and groups the error messages in a meaningful way.

Extension methods

As a start, there are some simple extension method you can use.

Include the following using statement:

using SimpleValidator.Extensions;

Then you should have some useful helper methods as shown below:

string email = "test@email.com";
if (email.IsLength(5)) ...
if (email.IsEmail()) ...
if (email.IsMatch("test2@email.com")) ...
if (email.IsInt()) ...
if (email.IsNumber()) ...

The full list of extension methods is as follows:

// string based extension methods (IsNotNullOrEmpty and IsNotNullOrWhiteSpace are the only methods that have inverse tests. The assumption is to always assume the positive.
[string].IsNotNullOrEmpty
[string].IsNullOrEmpty
[string].IsNotNullOrWhiteSpace
[string].IsNullOrWhiteSpace();
[string].IsBetweenLength
[string].IsMaxLength
[string].IsMinLength
[string].IsExactLength
[string].IsEmail
[string].IsPassword
[string].IsCreditCard
[string].IsEqualTo
// object based extension methods
[object].IsNotNull
[object].IsNull
[object].Is
[object].IsNot
[object].IsRule // validating against an interface for more complex business rules
[object].IsInt
[object].IsShort
[object].IsLong
[object].IsDouble
[object].IsBool
[object].IsNumber
[object].Is<T>
// integer (same dor double, decimal, short and long)
[integer].IsNotZero
[integer].IsEqualTo
[integer].IsGreaterThan
[integer].IsLessThan
[integer].IsBetween
// datetime
[datetime].IsDate
[datetime].IsGreaterThan
[datetime].IsGreaterThanOrEqualTo
[datetime].IsLessThan
[datetime].IsLessThanOrEqualTo
[datetime].IsEqualTo
[datetime].IsBetween
[datetime].IsGreaterThanDateOnly
[datetime].IsGreaterThanOrEqualToDateOnly
[datetime].IsLessThanDateOnly
[datetime].IsLessThanOrEqualToDateOnly
[datetime].IsEqualToDateOnly
[datetime].IsBetweenDateOnly

From here you also have access to some helpful conversion methods, e.g.

string input = "5";
if (input.IsInt())
return input.ToInt();

A full list of these are as follows

.ToInt()
.ToShort()
.ToLong()
.ToDouble()
.ToDecimal()
.ToBool()
// and then a more generic one
.To<T>()

Validator

Extension methods work great for quick simple checks, but you probably want to do more complex validation.

For this, lets create a Validator object. From there you can use it to validate several properties of an object with a few overrides as shown below

Remember to include using SimpleValidator;

For each text method you have the following overrides:

SimpleValidator.Validator validator = new Validator();
// value
validator.IsEmail("test@email.com");
// name, value
validator.IsEmail("Email", "test@email.com");
// name, value, message
validator.IsEmail("Email", "test@email.com", "Please enter a valid email address");
if (validator.IsValid())
{
// everything is good
}
else
{
// validator.Errors contains a name / message dictionary of your errors }

It supports chaining so you could write it as follows

validator
.IsEmail("test@email.com")
.IsEmail("Email", "test@email.com", "This is an error message")
.IsEmail("Email", "test@email.com").WithMessage("This is an error message")

This is quite usefull for more complex object validation e.g.

// validate the name
validator
.NotNull(name).WithMessage("Name cannot be an empty string")
.IsLength(name, 5).WithMessage("Name must be at least 5 characters long")
.IsLength(name, 5, 20).WithMessage("Name must between 5 and 20 characters")
.Must(() =>
{
return name.Contains("abc");
}).WithMessage("Name cannot contain abc")
.MustNot(() =>
{
return name.Contains("bob");
}).WithMessage("Name cannot contain bob");

Each of the messages are added to an Error collection within the validator object. It also has a IsValid property. The validation is done as the method is called, which means you can stagger the validation if needed e.g.

string name = "Gordon";
// validate the name
validator
.NotNull(name).WithMessage("Name cannot be an empty string");
if (validator.IsValid)
{
// do something here that is dependant on name not being null
validator.Must(() =>
{
// some other condition
return true;
});
if (validator.IsValid)
{
// and down the rabit hole we go...
}
}

IRule

For more complex validation you can use the Is and IsNot validation methods passing in a function which should return a bool. For more serious business rule validations, you can pass in any object implementing the IRule interface to the IsRule(...) method. All this combined gives you some flexibility on how you go about implementing your validation.

Point is, every service method should implement validation for every incoming model.

Ok, I have validation errors, now what?

Implementation

Let's say we have a method

private User AddUser(UserModel user)
{
}

You pass in a UserModel, and if all goes well, the user is added and it returns a User object.

What happens if we encounter validation errors? Once approach is to throw an exception (Not ideal but works) and the library has an exception ready for this.

ValidationException


try
{
// ...
ServiceManager.AddUser(model);
// ...
}
catch (ValidationException ex)
{
// validation error - The validator is passed in as part of the exception so you can access the errors ex.Validator.Errors
}
catch (Exception ex)
{
// something else... woops
}

And in your validation method.

private User AddUser(UserModel user)
{
Validator validator = new Validator();
// validation happens here...
if (!validator.IsValid)
{
validator.ThrowValidationException();
}
// or you can do this without the IsValid check (it checks it internally)
validator.ThrowValidationExceptionIfInvalid();
}

ValidationMethodResult

Another approach is to use the ValidationMethodResult. Changing the above code it now looks as follows

public ValidationMethodResult<User> AddUser(UserModel model)
{
Validator validator = new Validator();
// do validation
return new ValidationMethodResult<User>(validator, user);
}

And then in your calling method

try
{
// ...
var result = ServiceManager.AddUser(model);
if (result.IsValid())
{
// all good and you have access to the return user
return Content(result.Result.Name); // The return users name...
}
else
{
// show errors using result.Errors
}
// ...
}
catch (Exception ex)
{
// something else... woops
}

It's a cleaner approach, allows you to specify complex return types and provides a boolean indication reflecting the validation state of the current object. You could use simple out param's and use a boolean return type for all methods, but I'm not a fan (That doesn't mean it's not right)

About

A simple .Net validation library written in C#

Resources

Stars

36 stars

Watchers

6 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

41 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SimpleValidator

A simple .Net validation library written in C#

It's based largely on the FluentValidation libary with some changes in it's usage.

Why SimpleValidator?

I needed a validation library that satisfied the following criteria:

  • Quick and simple validation checks
  • Complex business rule validation
  • Multiple stages of validation (i.e. do the simple stuff first, then more complex validation, but don't bother with the complex stuff if the simple stuff fails)
  • Avoid attribute based validation (I try to avoid using attributes. People think, I've validated my MVC model so my service method should be good to go. Not the case. Service methods should always validate everything as you cannot guarantee validation has been performed)
  • Readable
  • Central

What I came up with was the following...

As there is quite a bit to cover I'm going to start simply, and add complexity.

Structure

There is 2 types of validation you can perform using SimpleValidator. Extension methods and Validator. The validator essentially uses the extension methods behind the scenes but provides a useful wrapper for doing your validations and groups the error messages in a meaningful way.

Extension methods

As a start, there are some simple extension method you can use.

Include the following using statement:

using SimpleValidator.Extensions;

Then you should have some useful helper methods as shown below:

string email = "test@email.com";
if (email.IsLength(5)) ...
if (email.IsEmail()) ...
if (email.IsMatch("test2@email.com")) ...
if (email.IsInt()) ...
if (email.IsNumber()) ...

The full list of extension methods is as follows:

// string based extension methods (IsNotNullOrEmpty and IsNotNullOrWhiteSpace are the only methods that have inverse tests. The assumption is to always assume the positive.
[string].IsNotNullOrEmpty
[string].IsNullOrEmpty
[string].IsNotNullOrWhiteSpace
[string].IsNullOrWhiteSpace();
[string].IsBetweenLength
[string].IsMaxLength
[string].IsMinLength
[string].IsExactLength
[string].IsEmail
[string].IsPassword
[string].IsCreditCard
[string].IsEqualTo
// object based extension methods
[object].IsNotNull
[object].IsNull
[object].Is
[object].IsNot
[object].IsRule // validating against an interface for more complex business rules
[object].IsInt
[object].IsShort
[object].IsLong
[object].IsDouble
[object].IsBool
[object].IsNumber
[object].Is<T>
// integer (same dor double, decimal, short and long)
[integer].IsNotZero
[integer].IsEqualTo
[integer].IsGreaterThan
[integer].IsLessThan
[integer].IsBetween
// datetime
[datetime].IsDate
[datetime].IsGreaterThan
[datetime].IsGreaterThanOrEqualTo
[datetime].IsLessThan
[datetime].IsLessThanOrEqualTo
[datetime].IsEqualTo
[datetime].IsBetween
[datetime].IsGreaterThanDateOnly
[datetime].IsGreaterThanOrEqualToDateOnly
[datetime].IsLessThanDateOnly
[datetime].IsLessThanOrEqualToDateOnly
[datetime].IsEqualToDateOnly
[datetime].IsBetweenDateOnly

From here you also have access to some helpful conversion methods, e.g.

string input = "5";
if (input.IsInt())
return input.ToInt();

A full list of these are as follows

.ToInt()
.ToShort()
.ToLong()
.ToDouble()
.ToDecimal()
.ToBool()
// and then a more generic one
.To<T>()

Validator

Extension methods work great for quick simple checks, but you probably want to do more complex validation.

For this, lets create a Validator object. From there you can use it to validate several properties of an object with a few overrides as shown below

Remember to include using SimpleValidator;

For each text method you have the following overrides:

SimpleValidator.Validator validator = new Validator();
// value
validator.IsEmail("test@email.com");
// name, value
validator.IsEmail("Email", "test@email.com");
// name, value, message
validator.IsEmail("Email", "test@email.com", "Please enter a valid email address");
if (validator.IsValid())
{
// everything is good
}
else
{
// validator.Errors contains a name / message dictionary of your errors }

It supports chaining so you could write it as follows

validator
.IsEmail("test@email.com")
.IsEmail("Email", "test@email.com", "This is an error message")
.IsEmail("Email", "test@email.com").WithMessage("This is an error message")

This is quite usefull for more complex object validation e.g.

// validate the name
validator
.NotNull(name).WithMessage("Name cannot be an empty string")
.IsLength(name, 5).WithMessage("Name must be at least 5 characters long")
.IsLength(name, 5, 20).WithMessage("Name must between 5 and 20 characters")
.Must(() =>
{
return name.Contains("abc");
}).WithMessage("Name cannot contain abc")
.MustNot(() =>
{
return name.Contains("bob");
}).WithMessage("Name cannot contain bob");

Each of the messages are added to an Error collection within the validator object. It also has a IsValid property. The validation is done as the method is called, which means you can stagger the validation if needed e.g.

string name = "Gordon";
// validate the name
validator
.NotNull(name).WithMessage("Name cannot be an empty string");
if (validator.IsValid)
{
// do something here that is dependant on name not being null
validator.Must(() =>
{
// some other condition
return true;
});
if (validator.IsValid)
{
// and down the rabit hole we go...
}
}

IRule

For more complex validation you can use the Is and IsNot validation methods passing in a function which should return a bool. For more serious business rule validations, you can pass in any object implementing the IRule interface to the IsRule(...) method. All this combined gives you some flexibility on how you go about implementing your validation.

Point is, every service method should implement validation for every incoming model.

Ok, I have validation errors, now what?

Implementation

Let's say we have a method

private User AddUser(UserModel user)
{
}

You pass in a UserModel, and if all goes well, the user is added and it returns a User object.

What happens if we encounter validation errors? Once approach is to throw an exception (Not ideal but works) and the library has an exception ready for this.

ValidationException


try
{
// ...
ServiceManager.AddUser(model);
// ...
}
catch (ValidationException ex)
{
// validation error - The validator is passed in as part of the exception so you can access the errors ex.Validator.Errors
}
catch (Exception ex)
{
// something else... woops
}

And in your validation method.

private User AddUser(UserModel user)
{
Validator validator = new Validator();
// validation happens here...
if (!validator.IsValid)
{
validator.ThrowValidationException();
}
// or you can do this without the IsValid check (it checks it internally)
validator.ThrowValidationExceptionIfInvalid();
}

ValidationMethodResult

Another approach is to use the ValidationMethodResult. Changing the above code it now looks as follows

public ValidationMethodResult<User> AddUser(UserModel model)
{
Validator validator = new Validator();
// do validation
return new ValidationMethodResult<User>(validator, user);
}

And then in your calling method

try
{
// ...
var result = ServiceManager.AddUser(model);
if (result.IsValid())
{
// all good and you have access to the return user
return Content(result.Result.Name); // The return users name...
}
else
{
// show errors using result.Errors
}
// ...
}
catch (Exception ex)
{
// something else... woops
}

It's a cleaner approach, allows you to specify complex return types and provides a boolean indication reflecting the validation state of the current object. You could use simple out param's and use a boolean return type for all methods, but I'm not a fan (That doesn't mean it's not right)

About

A simple .Net validation library written in C#

Resources

Stars

36 stars

Watchers

6 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

41 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SimpleValidator

A simple .Net validation library written in C#

It's based largely on the FluentValidation libary with some changes in it's usage.

Why SimpleValidator?

I needed a validation library that satisfied the following criteria:

  • Quick and simple validation checks
  • Complex business rule validation
  • Multiple stages of validation (i.e. do the simple stuff first, then more complex validation, but don't bother with the complex stuff if the simple stuff fails)
  • Avoid attribute based validation (I try to avoid using attributes. People think, I've validated my MVC model so my service method should be good to go. Not the case. Service methods should always validate everything as you cannot guarantee validation has been performed)
  • Readable
  • Central

What I came up with was the following...

As there is quite a bit to cover I'm going to start simply, and add complexity.

Structure

There is 2 types of validation you can perform using SimpleValidator. Extension methods and Validator. The validator essentially uses the extension methods behind the scenes but provides a useful wrapper for doing your validations and groups the error messages in a meaningful way.

Extension methods

As a start, there are some simple extension method you can use.

Include the following using statement:

using SimpleValidator.Extensions;

Then you should have some useful helper methods as shown below:

string email = "test@email.com";
if (email.IsLength(5)) ...
if (email.IsEmail()) ...
if (email.IsMatch("test2@email.com")) ...
if (email.IsInt()) ...
if (email.IsNumber()) ...

The full list of extension methods is as follows:

// string based extension methods (IsNotNullOrEmpty and IsNotNullOrWhiteSpace are the only methods that have inverse tests. The assumption is to always assume the positive.
[string].IsNotNullOrEmpty
[string].IsNullOrEmpty
[string].IsNotNullOrWhiteSpace
[string].IsNullOrWhiteSpace();
[string].IsBetweenLength
[string].IsMaxLength
[string].IsMinLength
[string].IsExactLength
[string].IsEmail
[string].IsPassword
[string].IsCreditCard
[string].IsEqualTo
// object based extension methods
[object].IsNotNull
[object].IsNull
[object].Is
[object].IsNot
[object].IsRule // validating against an interface for more complex business rules
[object].IsInt
[object].IsShort
[object].IsLong
[object].IsDouble
[object].IsBool
[object].IsNumber
[object].Is<T>
// integer (same dor double, decimal, short and long)
[integer].IsNotZero
[integer].IsEqualTo
[integer].IsGreaterThan
[integer].IsLessThan
[integer].IsBetween
// datetime
[datetime].IsDate
[datetime].IsGreaterThan
[datetime].IsGreaterThanOrEqualTo
[datetime].IsLessThan
[datetime].IsLessThanOrEqualTo
[datetime].IsEqualTo
[datetime].IsBetween
[datetime].IsGreaterThanDateOnly
[datetime].IsGreaterThanOrEqualToDateOnly
[datetime].IsLessThanDateOnly
[datetime].IsLessThanOrEqualToDateOnly
[datetime].IsEqualToDateOnly
[datetime].IsBetweenDateOnly

From here you also have access to some helpful conversion methods, e.g.

string input = "5";
if (input.IsInt())
return input.ToInt();

A full list of these are as follows

.ToInt()
.ToShort()
.ToLong()
.ToDouble()
.ToDecimal()
.ToBool()
// and then a more generic one
.To<T>()

Validator

Extension methods work great for quick simple checks, but you probably want to do more complex validation.

For this, lets create a Validator object. From there you can use it to validate several properties of an object with a few overrides as shown below

Remember to include using SimpleValidator;

For each text method you have the following overrides:

SimpleValidator.Validator validator = new Validator();
// value
validator.IsEmail("test@email.com");
// name, value
validator.IsEmail("Email", "test@email.com");
// name, value, message
validator.IsEmail("Email", "test@email.com", "Please enter a valid email address");
if (validator.IsValid())
{
// everything is good
}
else
{
// validator.Errors contains a name / message dictionary of your errors }

It supports chaining so you could write it as follows

validator
.IsEmail("test@email.com")
.IsEmail("Email", "test@email.com", "This is an error message")
.IsEmail("Email", "test@email.com").WithMessage("This is an error message")

This is quite usefull for more complex object validation e.g.

// validate the name
validator
.NotNull(name).WithMessage("Name cannot be an empty string")
.IsLength(name, 5).WithMessage("Name must be at least 5 characters long")
.IsLength(name, 5, 20).WithMessage("Name must between 5 and 20 characters")
.Must(() =>
{
return name.Contains("abc");
}).WithMessage("Name cannot contain abc")
.MustNot(() =>
{
return name.Contains("bob");
}).WithMessage("Name cannot contain bob");

Each of the messages are added to an Error collection within the validator object. It also has a IsValid property. The validation is done as the method is called, which means you can stagger the validation if needed e.g.

string name = "Gordon";
// validate the name
validator
.NotNull(name).WithMessage("Name cannot be an empty string");
if (validator.IsValid)
{
// do something here that is dependant on name not being null
validator.Must(() =>
{
// some other condition
return true;
});
if (validator.IsValid)
{
// and down the rabit hole we go...
}
}

IRule

For more complex validation you can use the Is and IsNot validation methods passing in a function which should return a bool. For more serious business rule validations, you can pass in any object implementing the IRule interface to the IsRule(...) method. All this combined gives you some flexibility on how you go about implementing your validation.

Point is, every service method should implement validation for every incoming model.

Ok, I have validation errors, now what?

Implementation

Let's say we have a method

private User AddUser(UserModel user)
{
}

You pass in a UserModel, and if all goes well, the user is added and it returns a User object.

What happens if we encounter validation errors? Once approach is to throw an exception (Not ideal but works) and the library has an exception ready for this.

ValidationException


try
{
// ...
ServiceManager.AddUser(model);
// ...
}
catch (ValidationException ex)
{
// validation error - The validator is passed in as part of the exception so you can access the errors ex.Validator.Errors
}
catch (Exception ex)
{
// something else... woops
}

And in your validation method.

private User AddUser(UserModel user)
{
Validator validator = new Validator();
// validation happens here...
if (!validator.IsValid)
{
validator.ThrowValidationException();
}
// or you can do this without the IsValid check (it checks it internally)
validator.ThrowValidationExceptionIfInvalid();
}

ValidationMethodResult

Another approach is to use the ValidationMethodResult. Changing the above code it now looks as follows

public ValidationMethodResult<User> AddUser(UserModel model)
{
Validator validator = new Validator();
// do validation
return new ValidationMethodResult<User>(validator, user);
}

And then in your calling method

try
{
// ...
var result = ServiceManager.AddUser(model);
if (result.IsValid())
{
// all good and you have access to the return user
return Content(result.Result.Name); // The return users name...
}
else
{
// show errors using result.Errors
}
// ...
}
catch (Exception ex)
{
// something else... woops
}

It's a cleaner approach, allows you to specify complex return types and provides a boolean indication reflecting the validation state of the current object. You could use simple out param's and use a boolean return type for all methods, but I'm not a fan (That doesn't mean it's not right)

About

A simple .Net validation library written in C#

Resources

Stars

36 stars

Watchers

6 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

41 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SimpleValidator

A simple .Net validation library written in C#

It's based largely on the FluentValidation libary with some changes in it's usage.

Why SimpleValidator?

I needed a validation library that satisfied the following criteria:

  • Quick and simple validation checks
  • Complex business rule validation
  • Multiple stages of validation (i.e. do the simple stuff first, then more complex validation, but don't bother with the complex stuff if the simple stuff fails)
  • Avoid attribute based validation (I try to avoid using attributes. People think, I've validated my MVC model so my service method should be good to go. Not the case. Service methods should always validate everything as you cannot guarantee validation has been performed)
  • Readable
  • Central

What I came up with was the following...

As there is quite a bit to cover I'm going to start simply, and add complexity.

Structure

There is 2 types of validation you can perform using SimpleValidator. Extension methods and Validator. The validator essentially uses the extension methods behind the scenes but provides a useful wrapper for doing your validations and groups the error messages in a meaningful way.

Extension methods

As a start, there are some simple extension method you can use.

Include the following using statement:

using SimpleValidator.Extensions;

Then you should have some useful helper methods as shown below:

string email = "test@email.com";
if (email.IsLength(5)) ...
if (email.IsEmail()) ...
if (email.IsMatch("test2@email.com")) ...
if (email.IsInt()) ...
if (email.IsNumber()) ...

The full list of extension methods is as follows:

// string based extension methods (IsNotNullOrEmpty and IsNotNullOrWhiteSpace are the only methods that have inverse tests. The assumption is to always assume the positive.
[string].IsNotNullOrEmpty
[string].IsNullOrEmpty
[string].IsNotNullOrWhiteSpace
[string].IsNullOrWhiteSpace();
[string].IsBetweenLength
[string].IsMaxLength
[string].IsMinLength
[string].IsExactLength
[string].IsEmail
[string].IsPassword
[string].IsCreditCard
[string].IsEqualTo
// object based extension methods
[object].IsNotNull
[object].IsNull
[object].Is
[object].IsNot
[object].IsRule // validating against an interface for more complex business rules
[object].IsInt
[object].IsShort
[object].IsLong
[object].IsDouble
[object].IsBool
[object].IsNumber
[object].Is<T>
// integer (same dor double, decimal, short and long)
[integer].IsNotZero
[integer].IsEqualTo
[integer].IsGreaterThan
[integer].IsLessThan
[integer].IsBetween
// datetime
[datetime].IsDate
[datetime].IsGreaterThan
[datetime].IsGreaterThanOrEqualTo
[datetime].IsLessThan
[datetime].IsLessThanOrEqualTo
[datetime].IsEqualTo
[datetime].IsBetween
[datetime].IsGreaterThanDateOnly
[datetime].IsGreaterThanOrEqualToDateOnly
[datetime].IsLessThanDateOnly
[datetime].IsLessThanOrEqualToDateOnly
[datetime].IsEqualToDateOnly
[datetime].IsBetweenDateOnly

From here you also have access to some helpful conversion methods, e.g.

string input = "5";
if (input.IsInt())
return input.ToInt();

A full list of these are as follows

.ToInt()
.ToShort()
.ToLong()
.ToDouble()
.ToDecimal()
.ToBool()
// and then a more generic one
.To<T>()

Validator

Extension methods work great for quick simple checks, but you probably want to do more complex validation.

For this, lets create a Validator object. From there you can use it to validate several properties of an object with a few overrides as shown below

Remember to include using SimpleValidator;

For each text method you have the following overrides:

SimpleValidator.Validator validator = new Validator();
// value
validator.IsEmail("test@email.com");
// name, value
validator.IsEmail("Email", "test@email.com");
// name, value, message
validator.IsEmail("Email", "test@email.com", "Please enter a valid email address");
if (validator.IsValid())
{
// everything is good
}
else
{
// validator.Errors contains a name / message dictionary of your errors }

It supports chaining so you could write it as follows

validator
.IsEmail("test@email.com")
.IsEmail("Email", "test@email.com", "This is an error message")
.IsEmail("Email", "test@email.com").WithMessage("This is an error message")

This is quite usefull for more complex object validation e.g.

// validate the name
validator
.NotNull(name).WithMessage("Name cannot be an empty string")
.IsLength(name, 5).WithMessage("Name must be at least 5 characters long")
.IsLength(name, 5, 20).WithMessage("Name must between 5 and 20 characters")
.Must(() =>
{
return name.Contains("abc");
}).WithMessage("Name cannot contain abc")
.MustNot(() =>
{
return name.Contains("bob");
}).WithMessage("Name cannot contain bob");

Each of the messages are added to an Error collection within the validator object. It also has a IsValid property. The validation is done as the method is called, which means you can stagger the validation if needed e.g.

string name = "Gordon";
// validate the name
validator
.NotNull(name).WithMessage("Name cannot be an empty string");
if (validator.IsValid)
{
// do something here that is dependant on name not being null
validator.Must(() =>
{
// some other condition
return true;
});
if (validator.IsValid)
{
// and down the rabit hole we go...
}
}

IRule

For more complex validation you can use the Is and IsNot validation methods passing in a function which should return a bool. For more serious business rule validations, you can pass in any object implementing the IRule interface to the IsRule(...) method. All this combined gives you some flexibility on how you go about implementing your validation.

Point is, every service method should implement validation for every incoming model.

Ok, I have validation errors, now what?

Implementation

Let's say we have a method

private User AddUser(UserModel user)
{
}

You pass in a UserModel, and if all goes well, the user is added and it returns a User object.

What happens if we encounter validation errors? Once approach is to throw an exception (Not ideal but works) and the library has an exception ready for this.

ValidationException


try
{
// ...
ServiceManager.AddUser(model);
// ...
}
catch (ValidationException ex)
{
// validation error - The validator is passed in as part of the exception so you can access the errors ex.Validator.Errors
}
catch (Exception ex)
{
// something else... woops
}

And in your validation method.

private User AddUser(UserModel user)
{
Validator validator = new Validator();
// validation happens here...
if (!validator.IsValid)
{
validator.ThrowValidationException();
}
// or you can do this without the IsValid check (it checks it internally)
validator.ThrowValidationExceptionIfInvalid();
}

ValidationMethodResult

Another approach is to use the ValidationMethodResult. Changing the above code it now looks as follows

public ValidationMethodResult<User> AddUser(UserModel model)
{
Validator validator = new Validator();
// do validation
return new ValidationMethodResult<User>(validator, user);
}

And then in your calling method

try
{
// ...
var result = ServiceManager.AddUser(model);
if (result.IsValid())
{
// all good and you have access to the return user
return Content(result.Result.Name); // The return users name...
}
else
{
// show errors using result.Errors
}
// ...
}
catch (Exception ex)
{
// something else... woops
}

It's a cleaner approach, allows you to specify complex return types and provides a boolean indication reflecting the validation state of the current object. You could use simple out param's and use a boolean return type for all methods, but I'm not a fan (That doesn't mean it's not right)

About

A simple .Net validation library written in C#

Resources

Stars

36 stars

Watchers

6 watching

Forks

Releases

Packages

Used by

Contributors

Languages