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 (
==
,!=
viaEqualOperator
andNotEqualOperator
)
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
GetEqualityComponents
to yield the fields that determine equality - Optionally define
==
and!=
operators in the derived class usingEqualOperator
andNotEqualOperator
.
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.