3komma14 / guard Goto Github PK
View Code? Open in Web Editor NEWCode guard in c#
License: Other
Code guard in c#
License: Other
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
You do not need to pass in the string name of the property, you might be able to use the [CallerMemberName] attribute.
Currently there's IsLessThan
and IsGreaterThan
.
IsLessThanOrEqualTo
and IsGreaterThanOrEqualTo
can be useful as well and are basically already used inside of IsInRange
.
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.
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? :)
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:
Guard.That<T>(T argument, string argumentName = "")
)TODO:
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.ArgNameFunc
1.GetArgName(Func1 argument) at Seterlund.CodeGuard.Internals.ArgNameFunc
1.get_Value()
at Seterlund.CodeGuard.Internals.ArgName.op_Implicit(ArgName argName)
at Seterlund.CodeGuard.Internals.ThrowMessageHandler1.SetArgumentNull() at Seterlund.CodeGuard.ClassValidatorExtensions.IsNotNull[T](IArg
1 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
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?
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(Expression
1 argument)
at Seterlund.CodeGuard.Internals.ArgBaseExpression1..ctor(Expression
1 argument)
at Seterlund.CodeGuard.Internals.ThrowOnFirstErrorArg1..ctor(Expression
1 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.
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:
Currently on 4.0 is compiled and added to tha package.
Add 3.5 also.
You can avoid CA1062 popping up by exposing a ValidatedNotNullAttribute in the library and then attributing it on the parameter in IsNotNull method. Probably won't work for expressions, but will work when the actual parameter is provided.
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! :)
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
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).
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.
Some love for R# would be awesome with this library. I often get warnings about implicitly captured enclosures and null reference exceptions.
You can give R# a hand with its analysis by providing annotations about the library. See http://www.hmemcpy.com/blog/2013/02/preventing-resharpers-implicitly-captured-closure-warning-in-fakeiteasy-unit-tests/ for an example.
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());
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
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.
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(IArg
1 arg)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.