Coder Social home page Coder Social logo

guard's Introduction

CodeGuard

A simple Guard and validator library made in c#. Library also available at NuGet.

Need more checks?? Please contribute or contact me.

Example of usage:

// --- using Guard.That(...) ---
// Guard.That that will throw an exception, when some condition is not met

public void SomeMethod(int arg1, int arg2)
{
	// This line will throw an exception when the arg1 is less or equal to arg2
	Guard.That(() => arg1).IsGreaterThan(arg2);

	// This will check that arg1 is not null and that is in some range 1..100
	Guard.That(arg2).IsNotNull().IsInRange(1,100);

	// Several checks can be added.
	Guard.That(arg1).IsInRange(100,1000).IsEven().IsTrue(x => x > 50, "Must be over 500");

	// Do stuff
}

// --- using Validate.That(...) ---
// Validate.That makes is possible to get a list of all error conditions

public void OtherMethod(int arg1)
{
	// Get a list of errors
	List<string> errors = Validate.That(() => arg1).IsNotNull().GetResult();
}

Incomplete list of checks:

The following checks are available. But the best documentation is currently the tests. New checks can easily be made by creating a extension method.

For object:

  • Is
  • IsNotDefault

For bool:

  • IsTrue
  • IsFalse

For class:

  • IsNotNull

For IComparable (Int32, Double, String, Char, DateTime and other classes implementing the interface)

  • IsEqual
  • IsNotEqual
  • IsGreatherThan
  • IslessThan
  • IsInRange

For int and long:

  • IsOdd
  • IsEven
  • IsPrime

For string:

  • IsNotEmpty
  • IsNotNullOrEmpty
  • StartsWith
  • EndsWith
  • Length
  • Contains
  • IsMatch

For IEnumerable:

  • IsNotEmpty
  • Length
  • Conatins

For Guid:

  • IsNotEmpty

guard's People

Contributors

3komma14 avatar brunojuchli avatar cecilphillip avatar redknightlois avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

guard's Issues

Checking whether argument name is known is cumbersome

Currently everyone interested in whether the argument name is known must perform the following:

string.IsNullOrEmpty(this._arg.Name))

Because messages should contain the argument name whenever possible, this kind of code may be duplicated a lot of times (also see #21 and #20).

I suggest amending the IArg<T> interface with a method:

bool HasName { get; }

(which performs the string.IsNullOrEmpty check).

Alternatives:

  • If it's deemed acceptable to have the same message whether the argument is known or not, we could just replace the "null" argument name with "unknown".
  • Maybe one could also enforce that the argument name must be known at all times (throw an exception if it's not specified => implications on Guard.That<T>(T argument, string argumentName = ""))

TODO:

  • Determine which way to go (provide easier checking or make checking obsolete. If making checking obsolete => which way to go)

Impossible to validate property value of a class

We created a class validation method using your CodeGuard library.
When we try to validate a property, we receive an ArgumentException with the following message and stack trace:

metadataToken Parameter name: Token 0x06000292 is not a valid FieldInfo token in the scope of module APECore.Dto.dll.

at System.Reflection.RuntimeModule.ResolveField(Int32 metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments) at Seterlund.CodeGuard.Internals.ArgBase``1.GetArgName(Func``1 argument) at Seterlund.CodeGuard.Internals.AccumulateErrorsArg``1..ctor(Func``1 argument) at APE.CrossCutting.Validation.ModelStateAccumulateErrorsArg``1..ctor(Func``1 argument, ModelStateDictionary modelState, String argName) in c:\\Users\\hprfbn\\Documents\\Visual Studio 2012\\Projects\\APE\\CrossCutting\\Validation\\ModelStateAccumulateErrorsArg.cs:line 20 at APE.CrossCutting.Validation.CodeGuardExtensions.ValidateThat[T](ModelStateDictionary modelState, Func``1 argument, String argName) in c:\\Users\\hprfbn\\Documents\\Visual Studio 2012\\Projects\\APE\\CrossCutting\\Validation\\CodeGuardExtensions.cs:line 32 at APECore.Dto.SalaireDto.Validate(IConfiguration configuration, ModelStateDictionary modelState) in c:\\Users\\hprfbn\\Documents\\Visual Studio 2012\\Projects\\APE\\APECore.Dto\\SalaireDto.cs:line 107 at APECore.Facades.Facade.ValidateDto(Object obj) in c:\\Users\\hprfbn\\Documents\\Visual Studio 2012\\Projects\\APE\\APECore\\Facades\\Facade.cs:line 45 at APECore.Facades.SalaireFacade.CheckAndSaveSalaire(SalaireDto salaire) in c:\\Users\\hprfbn\\Documents\\Visual Studio 2012\\Projects\\APE\\APECore\\Facades\\SalaireFacade.cs:line 91 at APE.Services.Controllers.Api.SalaireController.Post(SalaireDto salaireDto) in c:\\Users\\hprfbn\\Documents\\Visual Studio 2012\\Projects\\APE\\Web\\APE.Services\\Controllers\\Api\\SalaireController.cs:line 63 at lambda_method(Closure , Object , Object[] ) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass13.<GetExecutor>b__c(Object instance, Object[] methodParameters) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments) at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func``1 func, CancellationToken cancellationToken)

I created a gist with a code example that produce the error:
Gist

Version 2.3.0 breaks support for complex expressions

The previous version supported the following scenario.

        Guard.That(() => account).IsNotNull();
        Guard.That(() => account.Id).IsNotEmpty();

The current version throws the following exception for the second check:

System.InvalidCastException: Unable to cast object of type 'System.Linq.Expressions.FieldExpression' to type 'System.Linq.Expressions.ConstantExpression'.
Result StackTrace:
at Seterlund.CodeGuard.Internals.ArgBaseExpression1.GetValue(Expression1 argument)
at Seterlund.CodeGuard.Internals.ArgBaseExpression1..ctor(Expression1 argument)
at Seterlund.CodeGuard.Internals.ThrowOnFirstErrorArg1..ctor(Expression1 argument)

I get that this might not be the intended support for CodeGuard, but it really is/was nice. The alternative of Guard.That(account.Id, "account.Id").IsNotEmpty(); works, but isn't anywhere near as nice.

Strange result when running code coverage

Hi,

I ran into another issue when running code coverage.
I created a custom error arg by extending AccumulateErrorsArg class. This custom implementation uses a ModelStateDictionary to store the errors.

When I run the unit tests without code coverage, all the tests pass but when I enable code coverage, all the tests that emulate validation errors fail because the property name is not the expected one.

I created a Gist to show you the problem.

In the test method, I added a foreach to display all the keys of the ModelStateDictionary after running the validation method.
Unit test without code coverage gives the following output:

Key = Id

Unit test with code coverage gives the following output:

Key = StatementCount

Assert.IsTrue failed. 
   at Dtos.Test.CoutSalarialDtoTest.ApplVarDtoTest.TestIsNotValid() in CoutSalarialDtoTest.cs: line 44

As you can see, the key name is not the same so the unit test fails.
I use ReSharper for running the unit test and dotCover for the code coverage.

2.3.0 Guard.That throws NullReferenceException when name parameter not provided

The following code throws an NullReferenceException on the second check.

        Guard.That(() => account).IsNotNull();
        Guard.That(account.Id).IsNotEmpty();

There appears to be a default parameter value of "", but a NullReferenceException is thrown regardless.

NullReferenceException

System.NullReferenceException: Object reference not set to an instance of an object.
Result StackTrace:
at Seterlund.CodeGuard.Internals.ArgName.op_Implicit(ArgName argName)
at Seterlund.CodeGuard.Internals.ThrowMessageHandler1.Set(String message) at Seterlund.CodeGuard.GuidValidatorExtensions.IsNotEmpty(IArg1 arg)

ArgumentOutOfRange exception should contain valid range.

This issue depends on issues #21 and #22


Example:

Guard.That(() => foo).IsInRange(50,500);

results in an ArgumentOutOfRangeException where the ParamName property is set to "foo", however the exception message does not indicate the range nor the actual value.

The message should be improved to contain:

| The value 1337 of foo is not in its allowed range of 50 to 500

and where the parameter name is unknown:

| The value 1337 is not in its allowed range of 50 to 500

This should be done for all usages of IMessageHandler<T>.SetArgumentOutRange:

  • ArrayValidatorExtensions.CountIs
  • ComparableValidatorExtensions.IsEqual
  • ComparableValidatorExtensions.IsNotEqual
  • ComparableValidatorExtensions.IsGreaterThan
  • ComparableValidatorExtensions.IsLessThan
  • ComparableValidatorExtensions.IsInRange
  • IntegerValidatorExtensions.IsOdd
  • IntegerValidatorExtensions.IsEven
  • IntegerValidatorExtensions.IsPrime
  • IntegerValidatorExtensions.IsPositive
  • IntegerValidatorExtensions.IsNegative
  • BooleanValidatorExtensions.IsTrue
  • BooleanValidatorExtensions.IsFalse
  • BooleanValidatorExtensions.IsValid
  • EnumerableValidatorExtensions.Length

IsInRange does not include bounds

I would expected IsInRange(0,100) to treat 0 and 100 as valid values. It doesn't.
Why do i consider the bounds to be valid? Let's say i have valid values ranging from int.MinValue to 0.
(Also notice my use of the word ranging. Seams natural to include bounds, doesn't it?!)

With IsInRange i would not need to do something like IsInRange(int.MinValue-1,0). Now, of course, i cannot subtract 1 from int.MinValue (at least not without converting to a datatype with a larger range first).

Custom message for ArgumentOutOfRange exception

Allow extension methods for IArg<T> to set custom messages for ArgumentOutOfRangeException.
This requires adapting IMessageHandler<T>, so it has a method void SetArgumentOutRange(string message).

This method could either replace the existing SetArgumentOutRange(string message) or complement it.

TODO:

  • define whether new method should replace existing method or not

Exception when running code coverage

Hi,

When I run the code coverage on my unit tests, I receive ArgumentOutOfRangeException when a validation fail and the argument name must be retrieved either by GetFieldName() or by GetMethodName() method in ArgNameFunc<> class.

Here is the message and complete stack trace:

Test method Facade.Test.SalaireFacadeTest.CreatePropositionIsInvalid threw exception System.ArgumentOutOfRangeException, but exception APE.CrossCutting.Exceptions.UnprocessableEntityException was expected. Exception message: System.ArgumentOutOfRangeException: Token 0x01c37d18 is not valid in the scope of module APECore.Dto.dll.
Parameter name: metadataToken

   at System.Reflection.RuntimeModule.ResolveMethod(Int32 metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
   at System.Reflection.Module.ResolveMethod(Int32 metadataToken)
   at Seterlund.CodeGuard.Internals.ArgNameFunc`1.GetMethodName(Type targetType, MethodInfo method, Int32 handle)
   at Seterlund.CodeGuard.Internals.ArgNameFunc`1.GetArgName(Func`1 argument)
   at Seterlund.CodeGuard.Internals.ArgNameFunc`1.get_Value()
   at Seterlund.CodeGuard.Internals.ArgName.op_Implicit(ArgName argName)
   at Seterlund.CodeGuard.Internals.SaveMessageHandler`1.AddResultItem(String message)
   at Seterlund.CodeGuard.Internals.SaveMessageHandler`1.SetArgumentOutRange()
   at Seterlund.CodeGuard.ComparableValidatorExtensions.IsGreaterThan(IArg`1 arg, Func`1 param)
   at Seterlund.CodeGuard.ComparableValidatorExtensions.IsGreaterThan(IArg`1 arg, T param)
   at APECore.Dto.PropositionTauxOccupationDto.Validate(IConfiguration configuration, ModelStateDictionary modelState) in PropositionTauxOccupationDto.cs: line 125
   at APECore.Facades.Facade.ValidateDto(Object obj) in Facade.cs: line 45
   at APECore.Facades.SalaireFacade.CreateProposition(PropositionTauxOccupationDto propositionDto) in SalaireFacade.cs: line 281
   at Facade.Test.SalaireFacadeTest.CreatePropositionIsInvalid() in SalaireFacadeTest.cs: line 612

This exception is not thrown when running the tests without code coverage.

Code Contracts annotations

Including CodeGuard in code that uses Code Contracts does not work because post-conditions cannot be checked and requires explicit assumes.

Would you accept a PR for this?

Need a strongly named assembly!

Hello,

I'm using your wonderful library for Visual Studio extension (VSIX package), but i'm getting this error:

An exception of type 'System.IO.FileLoadException' occurred in Extension.dll but was not handled in user code

Additional information: Could not load file or assembly 'Seterlund.CodeGuard, Version=2.3.4.1324, Culture=neutral, PublicKeyToken=null' or one of its dependencies. A strongly-named assembly is required. (Exception from HRESULT: 0x80131044)

Can you make your assembly strongly named?

Thanks

Ability to specify custom exceptions on validation errors.

Something like this:

Implementation

public static IArg<T> WithExceptions<T>(this IArg<T> arg, Action<T, 
IEnumerable<ErrorInfo>> handleErrorsAction) where T : IComparable
        {
            if (arg.Errors.Any())
            {
                handleErrorsAction(arg.Value, arg.Errors);
            }

            return arg;
        }

Usage

var x = 1;
Validate.That(() => x).IsEqual(0).WithExceptions((value, errors) => throw new Exception());

Unexpected exception hit after upgrade from 2.2.1 to 2.2.7

We have hit an exception after upgrading to 2.2.7. The sanitized scenario is this:

this test:

[TestMethod]
public void ControllerThrowsExceptionWhenManagerNotProvidedTest()
{
Action action = () => new Controller(null);
action.ShouldThrow();
}

of this constructor:

public Controller(IManager manager)
{
Guard.That(() => manager).IsNotNull();
this._manager = manager;
}

Expected System.ArgumentNullException, but found System.ArgumentOutOfRangeException: Token 0x280a8501 is not valid in the scope of module MyNamespace.Service.dll.
Parameter name: metadataToken
at System.Reflection.RuntimeModule.ResolveField(Int32 metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
at System.Reflection.Module.ResolveField(Int32 metadataToken)
at Seterlund.CodeGuard.Internals.ArgNameFunc1.GetFieldName(Type targetType, MethodInfo method, Int32 handle) at Seterlund.CodeGuard.Internals.ArgNameFunc1.GetArgName(Func1 argument) at Seterlund.CodeGuard.Internals.ArgNameFunc1.get_Value()
at Seterlund.CodeGuard.Internals.ArgName.op_Implicit(ArgName argName)
at Seterlund.CodeGuard.Internals.ThrowMessageHandler1.SetArgumentNull() at Seterlund.CodeGuard.ClassValidatorExtensions.IsNotNull[T](IArg1 arg)
at MyNamespace.Service.Controllers.Controller..ctor(IManager manager) in c:\TFS_root\MyWorkspace\MyNamespace\Dev\Sprint 5\MyNamespace.Service\Controllers\Controller.cs:line 30
at MyNamespace.Service.UnitTests.Controllers.ControllerTests.b__0() in c:\TFS_root\MyWorkspace\MyNamespace\Dev\Sprint 5\MyNamespace.Service.UnitTests\Controllers\ControllerTests.cs:line 27
at FluentAssertions.Specialized.ActionAssertions.ShouldThrow[TException](String reason, Object[] reasonArgs) in c:\Workspaces\codeplex\FluentAssertions\Main\FluentAssertions.Net35\Specialized\ActionAssertions.cs:line 40.
Result StackTrace:
at FluentAssertions.Execution.LateBoundTestFramework.Throw(String message) in c:\Workspaces\codeplex\FluentAssertions\Main\FluentAssertions.Net35\Execution\LateBoundTestFramework.cs:line 25
at FluentAssertions.Execution.AssertionHelper.Throw(String message) in c:\Workspaces\codeplex\FluentAssertions\Main\FluentAssertions.Net35\Execution\AssertionHelper.cs:line 35
at FluentAssertions.Execution.Verification.FailWith(String failureMessage, Object[] failureArgs) in c:\Workspaces\codeplex\FluentAssertions\Main\FluentAssertions.Net35\Execution\Verification.cs:line 160
at FluentAssertions.Specialized.ActionAssertions.ShouldThrow[TException](String reason, Object[] reasonArgs) in c:\Workspaces\codeplex\FluentAssertions\Main\FluentAssertions.Net35\Specialized\ActionAssertions.cs:line 53
at FluentAssertions.AssertionExtensions.ShouldThrow[TException](Action action, String reason, Object[] reasonArgs) in c:\Workspaces\codeplex\FluentAssertions\Main\FluentAssertions.Net35\AssertionExtensions.cs:line 115
at MyNamespace.Service.UnitTests.Controllers.ControllerTests.ControllerThrowsExceptionWhenManagerNotProvidedTest() in c:\TFS_root\MyWorkspace\MyNamespace\Dev\Sprint 5\MyNamespace.Service.UnitTests\Controllers\ControllerTests.cs:line 29

Adding Annotations for ReSharper Code Inspection

Hi
Given the following code:

public static class ForEachExtensions
{
    public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
    {
        Guard.That(() => enumerable).IsNotNull();
        Guard.That(() => action).IsNotNull();

        foreach (T item in enumerable)
        {
            action(item);
        }
    }
}

ReSharper will warn us of "Possible multiple enumeration of IEnumerable". This is because - of course - ReSharper does not know whether the external .That() method is actually enumerating the IEnumerable or not.
However we can tell ReSharper that the That() method is not enumerating the IEnumerable. This can be done either by adding attributes to the implementation of Seterlund.CodeGuard or by adding an an XML file besides the Seterlund.CodeGuard.dll. The attributes don't require to reference a JetBrains library or the likes. Also see http://www.jetbrains.com/resharper/webhelp/Code_Analysis__Code_Annotations.html

Given that the code attributes don't require any external dependency and they clutter the code only a little, i think it might be a viable alternative. It would certainly be easiest to maintain. So at this point i personally would prefer this, but of course, the external XML annotations are good enough, too.

Would you accept a pull request for this feature and if so, which method would you prefer?

btw. Thank you for the great library! :)

Poor performance using expression overload on hot path

I'm really liking using this package. I've found however that I can't use the expression overloads on hot paths because they are not performing well.

For example, 87000 executions of three guard checks is taking 65% of the program execution time. Drop it down to the (instance, "name") overload it that figure drops down to effectively zero.

Are there any optimizations or caching of expression -> parameter names that can be done so I can have my cake and eat it? :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.