Fluxera.ValueObject 7.1.5

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package Fluxera.ValueObject --version 7.1.5                
NuGet\Install-Package Fluxera.ValueObject -Version 7.1.5                
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="Fluxera.ValueObject" Version="7.1.5" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Fluxera.ValueObject --version 7.1.5                
#r "nuget: Fluxera.ValueObject, 7.1.5"                
#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.
// Install Fluxera.ValueObject as a Cake Addin
#addin nuget:?package=Fluxera.ValueObject&version=7.1.5

// Install Fluxera.ValueObject as a Cake Tool
#tool nuget:?package=Fluxera.ValueObject&version=7.1.5                

Build Status

Fluxera.ValueObject

A value object implementation.

This library helps in implementing Value Object classes in the context of Domain-Driven Design. A Value Object has several traits, some of which this library provides.

A Value Object

  • is immutable. Every property must be read-only (i.e. no setter allowed) after instantiation.
  • contains domain logic and behaviours. It should encapsulate the domain complexity within it.
  • uses the Ubiquitous Language of the domain. A Value Object is an elegant way of embracing the language of the domain in the codebase.
  • exposes, uses and combines functions to provide domain value. Functions usually return new instances of a Value Object. Closure of Operations describes an operation whose return type is the same as the type of it's arguments.
// The following function doesn't change any given Amount instance, it just returns a new one.
public Amount Add(Amount amount) 
{
    if(this.Currency != amount.Currency)
    {
        throw new InvalidOperationException("Cannot add amounts with different currencies.");
    }

    Amount result = new Amount(this.Quantity + amount.Quantity, this.Currency);
    return result;
}
  • uses all of it's attibutes for Equality and Uniqueness.
  • is automatically validated upon instantiation using domain validation and throws exception if a validation fails.

Usage

ValueObject<TValueObject>

By having your Value Object derive from the ValueObject<TValueObject> base class it properly implements Equality (Equals()) and Uniqueness (GetHashCode()). Automatically all public properties are used for the calculations without you having to write a single line of code.

This default implementation uses reflection to aquire the metadata and to get the values to use. You can provide your own implementation simply by overriding the GetEqualityComponents() method.

A simple implementation would look like the this:

public class Amount : ValueObject<Amount>
{
    public Amount(decimal quantity, Currency currency)
    {
        this.Quantity = quantity;
        this.Currency = currency;
    }

    public decimal Quantity { get; }

    public Currency Currency { get; }

    public Amount Add(Amount amount)
    {
        if(this.Currency != amount.Currency)
        {
            throw new InvalidOperationException("Cannot add amounts with different currencies.");
        }

        Amount result = new Amount(this.Quantity + amount.Quantity, this.Currency);
        return result;
    }
}

If you decide not to use the relection-based approach, you can simply override GetEqualityComponents() and return the values manually. Keep in mind, that all attibutes should be used for Equality and Uniqueness.

protected override IEnumerable<object> GetEqualityComponents()
{
    yield return this.Quantity;
    yield return this.Currency;
}

PrimitiveValueObject<TValueObject, TValue

A specialized Value Object that only holds a single primitive, string or enum value.

Collections

There are implementations of IList<T>, ISet<T> and IDictionary<TKey, TValue> that determine equality based on content and not on the collection reference.

When your Value Object contains a collection, this collection needs to be wrapped in one of the available value collection to support the correct way for equality.

If you use the default behavior you can just wrap the collection in the constructor like below. The default equality behavior will automatically pick the value up.

public class Confederation : ValueObject<Confederation>
{
    public Confederation(string name, IList<Country> memberCountries)
    {
        Guard.Against.NullOrWhiteSpace(name, nameof(name));
        Guard.Against.NullOrEmpty(memberCountries, nameof(memberCountries));

        this.Name = name;
        this.MemberCountries = memberCountries.AsValueList(); // Wrap the list in a value list.
    }

    public string Name { get; }

    public IList<Country> MemberCountries { get; }
}

If you prefer to override GetEqualityComponents() and return the values manually you can wrap the list later.

protected override IEnumerable<object> GetEqualityComponents()
{
    yield return this.Name;
    yield return this.MemberCountries.AsValueList(); // Wrap the list in a value list.
}

Important Collections must be wrapped in value collections to ensure the correct equality behavior.

ValueList<T>

A list with equality based on the content instead on the list's reference, i.e. two different list instances containing the same items in the same order will be equal.

// Wrap an IList in a ValueList. 

IList<Country> countries = new List<Country> 
{
    Country.Create("DE"),
    Country.Create("US"),
};

IList<Country> valueList = countries.AsValueList();
ValueSet<T>

A set with equality based on the content instead on the set's reference, i.e two different set instances containing the same items will be equal regardless of their order.

// Wrap an ISet in a ValueSet. 

ISet<Country> countries = new HashSet<Country>
{
    Country.Create("DE"), 
    Country.Create("US"),
};

ISet<Country> valueSet = countries.AsValueSet();
ValueDictionary<TKey, TValue>

A dictionary with equality based on the content instead on the dictionary's reference, i.e. two different dictionary instances containing the same items will be equal.

// Wrap an IDictionary in a ValueDictionary. 

IDictionary<int, Country> countries = new Dictionary<int, Country>
{
    { 1, Country.Create("DE") }, 
    { 4, Country.Create("US") },
};

IDictionary<int, Country> valueDictionary = countries.AsValueDictionary();
Product Compatible and additional computed target framework versions.
.NET net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (9)

Showing the top 5 NuGet packages that depend on Fluxera.ValueObject:

Package Downloads
Fluxera.Repository.Abstractions

The abstractions for the generic repository implementation.

Fluxera.Repository

A generic repository implementation.

Fluxera.ValueObject.SystemTextJson

A libary that provides serializer support for System.Text.Json for value objects.

Fluxera.Extensions.Hosting.Modules.Domain.Shared

A module that enables the domain.

Fluxera.ValueObject.EntityFrameworkCore

A libary that provides serializer support for EF Core for value objects.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
9.0.1 0 11/16/2024
9.0.0 44 11/14/2024
8.2.4 776 11/1/2024
8.2.3 267 7/9/2024
8.2.2 1,862 6/15/2024
8.2.1 278 6/12/2024
8.2.0 286 6/12/2024
8.1.1 918 6/2/2024
8.1.0 910 5/26/2024
8.0.9 231 5/24/2024
8.0.8 196 5/24/2024
8.0.7 7,646 4/18/2024
8.0.6 1,237 4/13/2024
8.0.5 348 4/13/2024
8.0.4 985 3/19/2024
8.0.3 2,755 2/22/2024
8.0.2 6,503 1/4/2024
8.0.1 1,438 11/23/2023
8.0.0 1,422 11/15/2023
7.1.6 1,184 7/23/2023
7.1.5 482 7/20/2023
7.1.4 1,105 6/21/2023
7.1.3 5,205 4/13/2023
7.1.2 1,625 3/16/2023
7.1.1 2,078 2/24/2023
7.1.0 3,101 1/18/2023
7.0.6 4,024 12/22/2022
7.0.5 879 12/13/2022
7.0.4 1,414 12/13/2022
7.0.3 2,529 12/9/2022
7.0.2 1,276 11/15/2022
7.0.1 1,638 11/12/2022
7.0.0 1,160 11/9/2022
6.1.9 5,938 10/12/2022
6.1.8 21,285 9/15/2022
6.1.7 5,072 7/30/2022
6.1.6 5,138 6/30/2022
6.1.5 5,107 6/15/2022
6.1.4 6,249 6/7/2022
6.1.3 1,573 6/7/2022
6.1.2 4,645 6/1/2022
6.1.1 9,372 5/29/2022
6.1.0 3,458 5/28/2022
6.0.10 8,704 5/5/2022
6.0.9 2,617 4/20/2022
6.0.8 8,034 3/24/2022
6.0.7 1,684 2/17/2022
6.0.6 1,186 12/17/2021
6.0.5 302 12/9/2021
6.0.2 298 12/9/2021