EF Core Value Comparer — Complete Guide
In this tutorial, you'll learn about EF Core Value Comparer. We cover key concepts, practical examples, and best practices.
You have an entity with an owned collection or a custom value type. EF Core tracks changes by comparing values. For primitive types, comparison is built in. For custom types, EF Core uses reference equality by default — it never detects changes to mutable value objects.
Wrong
public class Product
{
public int Id { get; set; }
public string[] Tags { get; set; }
}
// Later:
product.Tags[0] = "updated"; // EF Core does not detect this change
Output: SaveChanges does not persist the change. EF Core compares string[] references — same array reference, no change detected.
Right
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>()
.Property(p => p.Tags)
.HasConversion(
v => string.Join(',', v),
v => v.Split(',', StringSplitOptions.RemoveEmptyEntries))
.HasConversion(new StringArrayConverter())
.Metadata.SetValueComparer(new ValueComparer<string[]>(
(c1, c2) => c1.SequenceEqual(c2), // Compare
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())), // Hash
c => c.ToArray())); // Snapshot
}
Output: Now EF Core detects changes to array elements and persists them.
For a reusable comparer:
public class StringArrayComparer : ValueComparer<string[]>
{
public StringArrayComparer()
: base(
(c1, c2) => c1.SequenceEqual(c2),
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
c => c.ToArray())
{ }
}
Prevention
- Use value comparers for collections, mutable structs, and custom value types.
- Provide three functions: equality comparison, hash code, and snapshot creation.
- Use
ValueComparer<T>with immutable types for copy-on-write semantics. - Use comparers with owned entity collections for change detection.
- Test that
SaveChangescorrectly detects modifications. - Use
IsKeyValueComparerfor composite key types.
Common Mistakes with core value comparer
- Using
returnto exit a function early instead of wrapping a pure value in the monad - Mixing let bindings with <- bindings in do notation, producing type errors
- Overlapping type class instances that cause GHC to reject the program with ambiguous dispatch errors
These mistakes appear frequently in real-world EF code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Value comparers are used in DodaTech for tracking changes to complex configuration objects. For more EF Core, visit DodaTech.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro