FrenchExDev.Object.Net 1.0.3

dotnet add package FrenchExDev.Object.Net --version 1.0.3
                    
NuGet\Install-Package FrenchExDev.Object.Net -Version 1.0.3
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FrenchExDev.Object.Net" Version="1.0.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FrenchExDev.Object.Net" Version="1.0.3" />
                    
Directory.Packages.props
<PackageReference Include="FrenchExDev.Object.Net" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add FrenchExDev.Object.Net --version 1.0.3
                    
#r "nuget: FrenchExDev.Object.Net, 1.0.3"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package FrenchExDev.Object.Net@1.0.3
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=FrenchExDev.Object.Net&version=1.0.3
                    
Install as a Cake Addin
#tool nuget:?package=FrenchExDev.Object.Net&version=1.0.3
                    
Install as a Cake Tool

FrenchExDev.Object.Net

NuGet NuGet GitHub Workflow Status

A tiny & flexible library to build and validate .NET objects.

Concepts

The library is built around two main concepts: object building and object validation. It provides abstract base classes that you can extend to create your own builders and validators for your specific objects.

Object Building

The library provides a way to build objects using a fluent interface. You can define how to create an object and its properties in a clear and concise manner.

public interface IObjectBuilder<TClass, TMember, TBuilder>
    where TClass : notnull
    where TMember : Enum
    where TBuilder : IObjectBuilder<TClass, TMember, TBuilder>, new()
{
    Task<TClass> BuildAsync(Dictionary<object, object>? visited = null, CancellationToken cancellationToken = default);
}

Object Validation

The library also offers a way to validate objects. You can define validation code for your objects and check if they meet the criteria.

public interface IObjectValidator<TClass>
{
    Task<IObjectValidation> ValidateAsync(TClass instance, CancellationToken cancellationToken = default);
}

public interface IObjectValidation
{

}

public interface IObjectMemberValidation
{

}

Example

The Subject

We use a simple class with a few properties to demonstrate the library's capabilities.

internal class TestClass : AbstractClass<TestClass.Member, TestClass>
{
    public enum Member
    {
        Value,
        AnotherValue,
        NestedObject
    }

    public int? Value { get; set; }
    public string? AnotherValue { get; set; } = string.Empty;

    public TestClass? NestedObject { get; set; }

    public override TestClass Set<T>(Member member, T? value) where T : default
    {
        switch (member)
        {
            case Member.Value:
                Value = (int?)(object?)value;
                break;
            case Member.AnotherValue:
                AnotherValue = (string?)(object?)value;
                break;
            case Member.NestedObject:
                NestedObject = (TestClass?)(object?)value;
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(member), member, null);
        }
        return this;
    }
}

The Subject' Builder

internal class TestBuilder : AbstractObjectBuilder<TestClass, TestClass.Member, TestBuilder>
    {
        private int? _value;
        private string? _anotherValue;
        private TestBuilder? _nestedObject;

        public TestBuilder WithValue(int? value)
        {
            _value = value;
            return this;
        }

        public TestBuilder WithAnotherValue(string? anotherValue)
        {
            _anotherValue = anotherValue;
            return this;
        }

        public TestBuilder WithNestedObject(TestBuilder? nestedObject)
        {
            _nestedObject = nestedObject;
            return this;
        }

        public TestBuilder WithNestedObject(Action<TestBuilder> builder)
        {
            _nestedObject = new TestBuilder();
            builder(_nestedObject);
            return this;
        }

        protected override async Task<TestClass> BuildInternalAsync(TestClass instance, Dictionary<object, object>? visited = null, CancellationToken cancellationToken = default)
        {
            foreach (var member in Enum.GetValues<TestClass.Member>())
            {
                switch (member)
                {
                    case TestClass.Member.Value:
                        instance.Set(TestClass.Member.Value, _value);
                        break;
                    case TestClass.Member.AnotherValue:
                        instance.Set(TestClass.Member.AnotherValue, _anotherValue);
                        break;
                    case TestClass.Member.NestedObject:
                        if (_nestedObject != null)
                        {
                            instance.Set(TestClass.Member.NestedObject, await _nestedObject.BuildAsync(visited, cancellationToken));
                        }
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }

            return instance;
        }
    }

The Subject' Validator

internal class TestValidator : AbstractObjectValidator<TestClass, TestClass.Member>
{
    protected override async Task ValidateInternalAsync(TestClass instance, ObjectValidation<TestClass.Member> dictionary, Dictionary<object, object> visited, CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(instance);
        ArgumentNullException.ThrowIfNull(dictionary);

        if (instance.NestedObject != null)
        {
            var nestedValidationResult = await ValidateAsync(instance.NestedObject, visited, cancellationToken);
            ArgumentNullException.ThrowIfNull(nestedValidationResult, nameof(nestedValidationResult));
            if (nestedValidationResult is ObjectValidation<TestClass.Member> nestedObjectValidationCast && !nestedObjectValidationCast.IsValid)
            {
                dictionary.Add(TestClass.Member.NestedObject, nestedValidationResult);
            }
        }

        if (instance.Value.HasValue && instance.Value.Value < 0)
        {
            dictionary.Add(TestClass.Member.Value, new FieldValidation<TestClass.Member, string, int?>("Value must be non-negative", TestClass.Member.Value, instance.Value));
        }

        if (instance.AnotherValue != null && instance.AnotherValue.Length < 5)
        {
            dictionary.Add(TestClass.Member.AnotherValue, new FieldValidation<TestClass.Member, string, string>("AnotherValue must be at least 5 characters long", TestClass.Member.AnotherValue, instance.AnotherValue));
        }
    }
}

Putting It All Together

[TestMethod]
public async Task CanBuildAndValidatComplexObjectWithCyclicReferences()
{
    var builder2 = new TestBuilder();
    var builder = new TestBuilder();

    builder.WithNestedObject(builder2).WithValue(1);
    builder2.WithValue(1).WithNestedObject(builder); // cyclic reference

    var instance = await builder.BuildAsync();
    var instance2 = await builder2.BuildAsync();

    var validator = new TestValidator();

    var validationResult = await validator.ValidateAsync(instance);
    validationResult.ShouldBeAssignableTo<ObjectValidation<TestClass.Member>>();

    var objectValidation = (ObjectValidation<TestClass.Member>)validationResult;

    objectValidation.ShouldNotBeNull();
    objectValidation.IsValid.ShouldBeTrue();
}

This example demonstrates how to use the FrenchExDev.Object.Net library to build and validate a simple object.

You can extend the builder and validator classes to suit your specific needs and create more complex objects and validation rules.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.3 39 8/23/2025
1.0.2 40 8/23/2025
1.0.0 36 8/23/2025