Skip to content

ValueObject

The ValueObject base class provides a foundation for implementing value objects in domain-driven design (DDD). Value objects are immutable and compare equality based on their atomic values, not by identity.

This class handles:

  • Correct equality checks (Equals)
  • Consistent hash code generation (GetHashCode)
  • Helper operators for equality (==, != via EqualOperator and NotEqualOperator)

Key Members

MemberDescription
EqualsChecks whether another object is equal based on equality components.
GetHashCodeComputes a hash code based on equality components.
EqualOperatorChecks equality for two ValueObject instances.
NotEqualOperatorChecks inequality for two ValueObject instances.
GetEqualityComponentsAbstract method to return the atomic values used for equality.

How to Use

To use this base class:

  1. Inherit from ValueObject
  2. Implement GetEqualityComponents to yield the fields that determine equality
  3. Optionally define == and != operators in the derived class using EqualOperator and NotEqualOperator.

Example

Here’s a simple Money value object as an example.

csharp
using System;
using System.Collections.Generic;

public class Money : ValueObject
{
    public decimal Amount { get; }
    public string Currency { get; }

    public Money(decimal amount, string currency)
    {
        Amount = amount;
        Currency = currency ?? throw new ArgumentNullException(nameof(currency));
    }

    protected override IEnumerable<object> GetEqualityComponents()
    {
        yield return Amount;
        yield return Currency;
    }

    public static bool operator ==(Money left, Money right)
    {
        return EqualOperator(left, right);
    }

    public static bool operator !=(Money left, Money right)
    {
        return NotEqualOperator(left, right);
    }
}

Example Usage

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

Console.WriteLine(money1 == money2); // True - same amount and currency
Console.WriteLine(money1 == money3); // False - different amount
Console.WriteLine(money1.Equals(money2)); // True

Using Value Objects with EF Core

Entity Framework Core does not have native support for complex value objects by default. However, you can map them as owned entities or by using conversion.

Option 1: Owned Types

Example: register Money as an owned type in your DbContext.

csharp
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Order>(builder =>
    {
        builder.OwnsOne(o => o.TotalPrice);
    });
}

Usage in an Entity:

csharp
public class Order
{
    public int Id { get; set; }

    public Money TotalPrice { get; set; } = default!;
}

Option 2: Value Conversion

For simpler scalar value objects (e.g., a Email or PhoneNumber), you can use a ValueConverter:

csharp
modelBuilder
    .Entity<User>()
    .Property(u => u.Email)
    .HasConversion(
        v => v.Value, // to store
        v => new Email(v)); // to read

Best Practices

  • Always make your value objects immutable.
  • Always include all properties that define equality in GetEqualityComponents.
  • Implement == and != for syntactic convenience.