Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggested additions to the repo #1

Open
wants to merge 54 commits into
base: review
Choose a base branch
from
Open

Suggested additions to the repo #1

wants to merge 54 commits into from

Conversation

domsinclair
Copy link
Collaborator

First set of e mail articles for review

At this point it's not uncommon for new or relatively inexperienced developers to create a simple checking method to satisfy the immediate requirement.

```c#
bool ValidateString(string input)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be clearer to throw an exception since this is what the aspect is doing.

}
```

If the requirement was that the string be no less than 10 characters in length and no more than 16 then undeniably it will produce a result. If at some point further on in the program another similar check is required then it's very easy to see how this might be copied and pasted with a a couple of alterations to match the next requirement.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This paragraph is not clear especially "it will produce a result" but having a more realistic example above would help.

{
try
{
Console.WriteLine("Enter your Password: ");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I think the example above (the first one) should align with this code.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And that of course was exactly where I got distracted and made the pull request to the contracts!

}
```

The benefit of using Metalama to handle such tasks is that they are handled consistently whilst your own code remains concise, easy to read and comprehend.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be good at this point to say that the same aspect can be used on parameters, properties or return values, with one more example when set on a parameter.

Copy link
Collaborator Author

@domsinclair domsinclair Feb 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok the rewritten ct1 is here. Apologies as my lack of git skills coming to the fore again!!

Common Tasks: Input / Output Validation

Many developers will be familiar with the phrase 'Garbage in, garbage out'. In essence if the input being entered into an application is rubbish one shouldn't be surprised if what comes out is rubbish as well. To avoid this developers' need to ensure that what goes into their application's routines meet acceptable criteria and equally what comes out does the same.

Validation is a task that every developer will face at some point and the approach that they take is more often than not a good indication of their overall development experience.

As an example consider a basic requirement that a given string must fall within a given number of characters.

A new or relatively inexperienced developers might create a simple checking method to satisfy the immediate requirement.


bool ValidateString(string input)
{
    return input.length < 10 && input.length > 16;
}

If the requirement was that the string be no less than 10 characters in length and no more than 16 then this very simple validation will at least provide a answer to the basic question 'Does this string fall within the defined character length?' However it doesn't really handle failure. Over time developers' will learn how to approach this properly but they will still find themselves having to take differing approaches depending on whether they are validating parameter inputs, properties or results.

Using Metalama tasks like this can be solved easily. It has a patterns library that itself has a number of pre-made contracts for a wide range of scenarios including the example under discussion.

You could write code as follows (using a simple console application as an example) employing no more than a simple attribute;


using Metalama.Patterns.Contracts;

namespace CommonTasks
{
    internal class Program
    {

        [StringLength(10, 16)]
        private static string? password;

        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("Enter your Password:  ");
                password = Console.ReadLine();
                Console.WriteLine("Your password meets the basic length requirement.");
            } catch(ArgumentException ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

Metalama's StringLength aspect takes as parameters either a maximum, or a minimum and maximum length and in the event of a validation failure throws a System.ArgumentException.

At the point of compilation it adds the necessary logic into your code.


using Metalama.Patterns.Contracts;

namespace CommonTasks
{
    internal class Program
    {


        private static string? _password1;


        [StringLength(10, 16)]
        private static string? password
        {
            get
            {
                return Program._password1;


            }
            set
            {
                if (value != null && (value!.Length < 10 || value.Length > 16))
                {
                    throw new ArgumentException($"The  'password' property must be a string with length between {10} and {16}.", "value");
                }
                Program._password1 = value;


            }
        }
        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("Enter your Password:  ");
                password = Console.ReadLine();
                Console.WriteLine("Your password meets the basic length requirement.");
            }
            catch (ArgumentException ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

There are numerous benefits to using Metalama contracts for validation. They are named in such a way that their intention is clear and where appropriate accept parameters that provide flexibility in the rules being tested. When validation fails it does so by throwing standard exceptions that are easy to handle. The real benefit though is that they can be used in exactly the same way to validate both inputs and outputs.

In the two examples that follow the task remains the same but instead of validating a property input parameters are validated in the first example , and the actual output in the second. In both cases the code that Metalama adds at compilation is also shown.


static string CreatePasswordValidatingInputs([StringLength(5,8)]string a, [StringLength(5, 8)] string b)
{
    return  a + b;
}

Which at compile time becomes;


     static string CreatePasswordValidatingInputs([StringLength(5,8)]string a, [StringLength(5, 8)] string b)
     {
         if (b.Length < 5 || b.Length > 8)
         {
             throw new ArgumentException($"The  'b' parameter must be a string with length between {5} and {8}.", "b");
         }
         if (a.Length < 5 || a.Length > 8)
         {
             throw new ArgumentException($"The  'a' parameter must be a string with length between {5} and {8}.", "a");
         }
         return a + b;

     }

And for outputs;


 [return: StringLength(10,16)]
 static string CreatePasswordValidatingOutput(string a, string b)
 {
     return a + b;
 }

Which at compile time becomes;

    [return: StringLength(10,16)]
   static string CreatePasswordValidatingOutput(string a, string b)
   {
       string returnValue;
       returnValue = a + b;


       if (returnValue.Length < 10 || returnValue.Length > 16)
       {
           throw new PostconditionViolationException($"The  return value must be a string with length between {10} and {16}.");
       }
       return returnValue;

   }

Exactly the same contract being used in ostensibly the same way via an attribute for three quite different validation scenarios but producing consistent code at compile time that the developer has not had to write by hand.

Whilst it should be pointed out that there is a StringLength attribute that forms part of the system.ComponentModel.DataAnnotations library it does not offer the same versatility oas that provide by Metalama.Patterns.Contracts in as much as it cannot be applied to return values and there is a necessity for the developer to provide their own error message.


If you'd like to know more about Metalama in general then visit our website.

You can learn more about Metalama contracts here.

Why not join us on Slack where you can keep up with what's new and get answers to any technical questions that you might have.

@@ -0,0 +1,103 @@
# Common Tasks: Meeting Requirements
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Common Tasks: Meeting Requirements
# Common Tasks: Verifying required parameters and fields

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense.

<br>

Syntax highlighting of specific Metalama keywords is also provided by this tool which is particularly useful when creating your own custom aspects.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also talk about the aspect explorer

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and CodeLens

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The gif does show the code lens, but an additional illustration would emphasise that. Obviously adding the Aspect Preview into this as well would make consummate sense.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed this in the announcements on the new preview.

⚠️ You must uninstall the Metalama extension before installing the new unified extension, as they will conflict if installed together.
⚠️ This feature is still not complete. The development experience is not final.
⚠️ Make sure to update your packages to the latest Metalama preview to enjoy the Aspect Explorer feature.

I had deliberately stuck to using the latest supported edition. Ergo maybe the aspect explorer would be better saved for a later email?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The email chain will be released with 2024.1 so it's fine to use any 2024.1 feature.

@@ -0,0 +1,190 @@
# Using Metalama: Inheritance (via [Inheritable])

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be moved to the "creating aspects" sequence.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually thought the same at first and then had second thoughts. I guess it boils down to this; If almost any metalama aspect could be made inheritable then I would argue that this is the right place, however if in reality it should only be used with a few specific aspect types then yes it would be more suited to to creating aspects.

@@ -0,0 +1 @@
# Common Tasks:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean something here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's for me to know and you to guess!!

Simply a placeholder for a new email that's all.


The benefit of using Metalama to handle such tasks is that they are handled consistently whilst your own code remains concise, easy to read and comprehend.

<br>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also say how it differs from system DataAnnotations.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Covered in revision added to earlier comment.


If you'd like to know more about Metalama in general then visit our [website](https://www.postsharp.net/metalama).

Why not join us on [Slack](https://www.postsharp.net/slack) where you can keep up with what's new and get answers to any technical questions that you might have.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would also need an article about validating references.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants