In this post, we will see the library ValueOf that we could use for validating our classes.
But first of all, what is the purpose of ValueOf?
From the GitHub page:
“The Smell: Primitive Obsession is using primitive data types to represent domain ideas. For example, we use a String to represent a message, an Integer to represent an amount of money, or a Struct/Dictionary/Hash to represent a specific object. The Fix: Typically, we introduce a ValueObject in place of the primitive data, then watch like magic as code from all over the system shows FeatureEnvySmell and wants to be on the new ValueObject. We move those methods, and everything becomes right with the world.
ValueOf lets you define ValueObject Types in a single line of code. Use them everywhere to strengthen your codebase.“
We start creating a Console application project and then, we add an entity called User so defined:
[USER.CS]
using System;
namespace TestValueOf.Domain.Entities
{
public class User
{
public Guid Id { get; init; } = Guid.NewGuid();
public string Name { get; init; }
public string Surname { get; init; }
public DateTime? Birthday { get; init; }
}
}
Then, we modify the file Program.cs in order to use the class User:
using System;
using TestValueOf.Domain.Entities;
namespace TestValueOf
{
internal class Program
{
static void Main(string[] args)
{
string name = string.Empty;
string surname = string.Empty;
string birthday = string.Empty;
Console.Write("Insert Name: ");
name = Console.ReadLine();
Console.Write("Insert Surname: ");
surname = Console.ReadLine();
Console.Write("Insert date of birth: ");
birthday = Console.ReadLine();
User objUser = new User() { Name = name, Surname = surname, Birthday = Convert.ToDateTime(birthday) };
GetPropertiesNameOfClass(objUser);
}
// Using the reflection, we can read properties and values of a class
public static void GetPropertiesNameOfClass(object objUser)
{
if (objUser != null)
{
foreach (var prop in objUser.GetType().GetProperties())
{
Console.WriteLine($"{prop.Name}: {prop.GetValue(objUser, null)}");
}
}
}
}
}
If we run the application, this will be the result:
Everything works fine but, without any validations, we could insert wrong input values:
In order to avoid this type of error, we should modify the code adding some validations:
using System;
using TestValueOf.Domain.Entities;
namespace TestValueOf
{
internal class Program
{
static void Main(string[] args)
{
string name = string.Empty;
string surname = string.Empty;
string birthday = string.Empty;
bool areParametersOk = true;
Console.Write("Insert Name: ");
name = Console.ReadLine();
Console.Write("Insert Surname: ");
surname = Console.ReadLine();
Console.Write("Insert date of birth: ");
birthday = Console.ReadLine();
if (name.Length == 0 || name.Length > 20)
{
areParametersOk = false;
Console.WriteLine("The name must not be null and can have at most 20 characters");
}
if (surname.Length == 0 || surname.Length > 25)
{
Console.WriteLine("The surname must not be null and can have at most 25 characters");
areParametersOk = false;
}
if (birthday.Length > 0)
{
try
{
if (Convert.ToDateTime(birthday) > DateTime.Today)
{
Console.WriteLine("The birthday must be less than today");
areParametersOk = false;
}
}
catch
{
Console.WriteLine("The birthday isn't a correct date value");
areParametersOk = false;
}
}
else
{
Console.WriteLine("The birthday must not be null");
areParametersOk = false;
}
if (areParametersOk)
{
User objUser = new User() { Name = name, Surname = surname, Birthday = Convert.ToDateTime(birthday) };
GetPropertiesNameOfClass(objUser);
}
}
// We define this method to read the properties of a class
// Using the reflection, we can use it for every type of class
public static void GetPropertiesNameOfClass(object objUser)
{
if (objUser != null)
{
foreach (var prop in objUser.GetType().GetProperties())
{
Console.WriteLine($"{prop.Name}: {prop.GetValue(objUser, null)}");
}
}
}
}
}
Now, if we run the application, these will be the results:
It works fine but, using the library ValueOf, we could optimize the code.
First of all, we install the library using the command from Package Manager Console:
install-package ValueOf
Then, we create three classes called Birthday, Name and Username that they will be the three User’ s properties:
[BIRTHDAY.CS]
using System;
using ValueOf;
namespace TestValueOf.Domain.Types
{
// Using ValueOf, we are saying that the 'value' of this class is a DateTime
public class Birthday : ValueOf<DateTime?, Birthday>
{
protected override void Validate()
{
string message = string.Empty;
if (Value == null)
{
message = "The Birthday must not be null";
}
else
{
try
{
if (Convert.ToDateTime(Value) > DateTime.Today)
{
message = "The Birthday must be less than today";
}
}
catch
{
message = "The Birthday isn't a correct date value";
}
}
if (message != string.Empty)
{
// if there is an error, system will throw an Exception
throw new Exception(message);
}
}
}
}
[NAME.CS]
using System;
using ValueOf;
namespace TestValueOf.Domain.Types
{
// Using ValueOf, we are saying that the 'value' of this class is a string
public class Name: ValueOf<string, Name>
{
protected override void Validate()
{
string message = string.Empty;
if (Value.Length == 0)
{
message = "The Name must not be null";
}
if (Value.Length > 20)
{
message = "The Name must be less of 20 character";
}
if(message!= string.Empty)
{
// if there is an error, system will throw an Exception
throw new Exception(message);
}
}
}
}
[SURNAME.CS]
using System;
using ValueOf;
namespace TestValueOf.Domain.Types
{
// Using ValueOf, we are saying that the 'value' of this class is a string
public class Surname : ValueOf<string, Surname>
{
protected override void Validate()
{
string message = string.Empty;
if (Value.Length == 0)
{
message = "The Surname must not be null";
}
if (Value.Length > 25)
{
message = "The Surname must be less of 20 character";
}
if (message != string.Empty)
{
// if there is an error, system will throw an Exception
throw new Exception(message);
}
}
}
}
Now, we modify the class User replacing the three properties with the new classes:
using System;
namespace TestValueOf
{
public class User
{
public Guid Id { get; init; } = Guid.NewGuid();
public Name Name { get; init; }
public Surname Surname { get; init; }
public Birthday Birthday { get; init; }
}
}
Finally, we modify Program.cs:
[PROGRAM.CS]
using System;
using TestValueOf.Domain.Entities;
using TestValueOf.Domain.Types;
namespace TestValueOf
{
internal class Program
{
static void Main(string[] args)
{
string name = string.Empty;
string surname = string.Empty;
string birthday = string.Empty;
Console.Write("Insert Name: ");
name = Console.ReadLine();
Console.Write("Insert Surname: ");
surname = Console.ReadLine();
Console.Write("Insert date of birth: ");
birthday = Console.ReadLine();
try
{
User objUser = new User() { Name = Name.From(name), Surname = Surname.From(surname), Birthday = Birthday.From(Convert.ToDateTime(birthday)) };
GetPropertiesNameOfClass(objUser);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
// We define this method to read the properties of a class
// Using the reflection, we can use it for every type of class
public static void GetPropertiesNameOfClass(object objUser)
{
if (objUser != null)
{
foreach (var prop in objUser.GetType().GetProperties())
{
Console.WriteLine($"{prop.Name}: {prop.GetValue(objUser, null)}");
}
}
}
}
}
We have done and now, if we run the application, these will be the results: