Sunlix.NET.Primitives 1.0.2

Prefix Reserved
dotnet add package Sunlix.NET.Primitives --version 1.0.2
                    
NuGet\Install-Package Sunlix.NET.Primitives -Version 1.0.2
                    
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="Sunlix.NET.Primitives" Version="1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Sunlix.NET.Primitives" Version="1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="Sunlix.NET.Primitives" />
                    
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 Sunlix.NET.Primitives --version 1.0.2
                    
#r "nuget: Sunlix.NET.Primitives, 1.0.2"
                    
#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 Sunlix.NET.Primitives@1.0.2
                    
#: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=Sunlix.NET.Primitives&version=1.0.2
                    
Install as a Cake Addin
#tool nuget:?package=Sunlix.NET.Primitives&version=1.0.2
                    
Install as a Cake Tool

Sunlix.NET.Primitives

.NET NuGet NuGet Downloads GitHub license

Sunlix.NET.Primitives is a lightweight library providing a set of key types for structured domain modeling in C#. It includes Entity, ValueObject, Enumeration, Unit, and Error, which are particularly useful in domain-driven design (DDD).

Features

EntityEnsures consistent identity handling for domain entities. An Entity is an object that has a distinct identity rather than being defined by its attributes. In domain-driven design (DDD), entities are central concepts that must be uniquely identifiable within the system.

Value ObjectAn immutable object with structural comparison. A ValueObject is an immutable object that represents a concept but has no unique identity. Two value objects are equal if their properties are equal.

EnumerationAn alternative to traditional enums that provides more flexibility. Unlike enums, which only represent a fixed set of named values, Enumeration allows each value to have additional properties and behavior. This eliminates the need for scattered switch statements, centralizes related logic, and makes the model easier to extend without modifying existing code, following the Open-Closed Principle (OCP).

UnitRepresents an absence of a meaningful result (functional programming). In functional programming, Unit is used as a return type to indicate that a function executes an action but does not return a meaningful value (like void).

ErrorStructured error representation with error codes and messages. Instead of using exceptions, Error enables structured error handling by encapsulating meaningful details.

Entity vs. Value Object

Entity (identifier equality)

Entities represent objects with a unique identity that remains constant throughout their lifecycle. Even if an entity’s properties change, it is still the same entity as long as its ID remains unchanged.

📌 Key Characteristics:
  • Has a unique identifier (Id).
  • Identity does not change even if properties change.
  • Compared based on identity (Id), not properties values.
  • Implements equality using Equals and GetHashCode based on Id.
📝 Example:
public class User : Entity<Guid>
{
    public string Name { get; }
    public User(Guid id, string name) : base(id) => Name = name;
}

var user1 = new User(Guid.NewGuid(), "Alice");
var user2 = new User(Guid.NewGuid(), "Alice");
Console.WriteLine(user1 == user2); // false, since IDs are different

Value Object (structural equality)

Value objects represent conceptual values rather than distinct entities. Their identity is defined by the combination of their properties values.

📌 Key Characteristics:
  • Does not have a unique identifier.
  • Completely interchangeable if their values are the same.
  • Compared by value (all compared properties must be equal).
  • Useful for representing concepts like monetary amounts, coordinates, or measurements.
📝 Example:
public class Money : ValueObject
{
    public decimal Amount { get; }
    public string Currency { get; }
    
    public Money(decimal amount, string currency)
    {
        Amount = amount;
        Currency = currency;
    }
    
    protected override IEnumerable<object> GetEqualityComponents()
    {
        yield return Amount;
        yield return Currency;
    }
}

var money1 = new Money(100, "USD");
var money2 = new Money(100, "USD");
var money3 = new Money(50, "USD");

Console.WriteLine(money1 == money2); // true, as values are identical
Console.WriteLine(money1 == money3); // false, since Amount is different

Equals Implementation

Why Implement IEquatable<T>?

Both Entity<TId> and ValueObject implement IEquatable<T>, which provides optimized equality checks. This has several benefits:

  • Improved Performance – Avoids unnecessary boxing/unboxing.
  • Type Safety – Prevents incorrect comparisons between unrelated types.
  • Better Integration with .NET Collections – Works well with LINQ, HashSet<T>, and Dictionary<TKey, TValue>.
📝 Entity Equals Implementation:
public override bool Equals(object? obj)
{
    if (ReferenceEquals(null, obj)) return false;
    if (ReferenceEquals(this, obj)) return true;
    if (obj.GetType() != this.GetType()) return false;
    return EqualsCore((Entity<TId>)obj);
}
📝 Value Object Equals Implementation:
private bool EqualsCore(ValueObject other)
    => this.GetEqualityComponents().SequenceEqual(other.GetEqualityComponents());

Enumeration

Enumeration is a more flexible alternative to standard C# enums. Unlike enums, which only represent a fixed set of named values, Enumeration allows defining additional properties and methods, such as retrieving values dynamically or encapsulating specific logic.

📝 Example:
public class OrderStatus : Enumeration
{
    public static readonly OrderStatus Pending = new(1, "Pending");
    public static readonly OrderStatus Shipped = new(2, "Shipped");
    public static readonly OrderStatus Delivered = new(3, "Delivered");
    
    private OrderStatus(int value, string displayName) : base(value, displayName) {}
}

The Problem with Using enum and switch

When using a standard enum, we often rely on switch statements, which become harder to maintain as new values are added.

📝 Example:
public enum SubscriptionPlan
{
    Free,
    Premium,
    Enterprise
}

public class SubscriptionService
{
    public decimal GetMonthlyFee(SubscriptionPlan plan)
    {
        switch (plan)
        {
            case SubscriptionPlan.Free:
                return 0m;
            case SubscriptionPlan.Premium:
                return 9.99m;
            case SubscriptionPlan.Enterprise:
                return 49.99m;
            default:
                throw new ArgumentOutOfRangeException(nameof(plan), "Unknown plan");
        }
    }
}
❌ Drawbacks of Using enum
  • Scattered logic – The GetMonthlyFee method relies on switch instead of being encapsulated in SubscriptionPlan itself.
  • Violates the Open-Closed Principle (OCP) – Adding a new subscription plan requires modifying multiple switch statements across the codebase.
✅ Using Enumeration to Eliminate switch.

Instead of relying on switch, we encapsulate logic directly within SubscriptionPlan.

📝 Example:
public abstract class SubscriptionPlan : Enumeration
{
    public static readonly SubscriptionPlan Free = new FreePlan();
    public static readonly SubscriptionPlan Premium = new PremiumPlan();
    public static readonly SubscriptionPlan Enterprise = new EnterprisePlan();

    protected SubscriptionPlan(int value, string displayName) : base(value, displayName) { }

    public abstract decimal MonthlyFee { get; }
    public abstract int MaxUsers { get; }

    private class FreePlan : SubscriptionPlan
    {
        public FreePlan() : base(0, "Free") { }

        public override decimal MonthlyFee => 0m;
        public override int MaxUsers => 1;
    }

    private class PremiumPlan : SubscriptionPlan
    {
        public PremiumPlan() : base(1, "Premium") { }

        public override decimal MonthlyFee => 9.99m;
        public override int MaxUsers => 5;
    }

    private class EnterprisePlan : SubscriptionPlan
    {
        public EnterprisePlan() : base(2, "Enterprise") { }

        public override decimal MonthlyFee => 49.99m;
        public override int MaxUsers => 100;
    }
}

Error

Error represents structured error handling with error codes and messages.

📝 Example:
public static class Errors
{
    public static readonly Error NotFound = new("404", "Resource not found");
    public static readonly Error ValidationError = new("400", "Invalid input provided");
}

Unit

Unit is a struct representing a void return type, commonly used in functional programming.

Example:
public Task<Unit> SaveChangesAsync()
{
    return Task.FromResult(Unit.value);
}

🚀 Installation

You can install the package via NuGet:

dotnet add package Sunlix.NET.Primitives

📄 License

Sunlix.NET.Primitives is licensed under the MIT License. See the LICENSE file for more details.

🤝 Contributing

Contributions are welcome! Feel free to open an issue or submit a pull request.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  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 is compatible.  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.  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.
  • net6.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • 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.2 97 2/21/2025
1.0.1 113 2/20/2025
1.0.0 106 2/18/2025