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 (
==,!=viaEqualOperatorandNotEqualOperator)
Key Members
| Member | Description |
|---|---|
Equals | Checks whether another object is equal based on equality components. |
GetHashCode | Computes a hash code based on equality components. |
EqualOperator | Checks equality for two ValueObject instances. |
NotEqualOperator | Checks inequality for two ValueObject instances. |
GetEqualityComponents | Abstract method to return the atomic values used for equality. |
How to Use
To use this base class:
- Inherit from
ValueObject - Implement
GetEqualityComponentsto yield the fields that determine equality - Optionally define
==and!=operators in the derived class usingEqualOperatorandNotEqualOperator.
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)); // TrueUsing 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 readBest Practices
- Always make your value objects immutable.
- Always include all properties that define equality in
GetEqualityComponents. - Implement
==and!=for syntactic convenience.