Skip to content

Commit 1a9ef8a

Browse files
authored
Updates to value converter docs (#3020)
Lots of examples! Fixes #614 Fixes #784 Part of #685 Fixes #2252 Fixes #2415 Fixes #2456 Fixes #2824 Fixes #2979
1 parent 391a35f commit 1a9ef8a

16 files changed

+2200
-127
lines changed

entity-framework/core/modeling/value-comparers.md

+11-28
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: Value Comparers - EF Core
33
description: Using value comparers to control how EF Core compares property values
44
author: ajcvickers
5-
ms.date: 03/20/2020
5+
ms.date: 01/16/2021
66
uid: core/modeling/value-comparers
77
---
88

@@ -16,9 +16,9 @@ uid: core/modeling/value-comparers
1616
1717
## Background
1818

19-
Change tracking means that EF Core automatically determines what changes were performed by the application on a loaded entity instance, so that those changes can be saved back to the database when <xref:Microsoft.EntityFrameworkCore.DbContext.SaveChanges%2A> is called. EF Core usually performs this by taking a *snapshot* of the instance when it's loaded from the database, and *comparing* that snapshot to the instance handed out to the application.
19+
[Change tracking](xref:core/change-tracking/index) means that EF Core automatically determines what changes were performed by the application on a loaded entity instance, so that those changes can be saved back to the database when <xref:Microsoft.EntityFrameworkCore.DbContext.SaveChanges%2A> is called. EF Core usually performs this by taking a *snapshot* of the instance when it's loaded from the database, and *comparing* that snapshot to the instance handed out to the application.
2020

21-
EF Core comes with built-in logic for snapshotting and comparing most standard types used in databases, so users don't usually need to worry about this topic. However, when a property is mapped through a [value converter](xref:core/modeling/value-conversions), EF Core needs to perform comparison on arbitrary user types, which may be complex. By default, EF Core uses the default equality comparison defined by types (e.g. the `Equals` method); for snapshotting, value types are copied to produce the snapshot, while for reference types no copying occurs, and the same instance is used as the snapshot.
21+
EF Core comes with built-in logic for snapshotting and comparing most standard types used in databases, so users don't usually need to worry about this topic. However, when a property is mapped through a [value converter](xref:core/modeling/value-conversions), EF Core needs to perform comparison on arbitrary user types, which may be complex. By default, EF Core uses the default equality comparison defined by types (e.g. the `Equals` method); for snapshotting, [value types](/dotnet/csharp/language-reference/builtin-types/value-types) are copied to produce the snapshot, while for [reference types](/dotnet/csharp/language-reference/keywords/reference-types) no copying occurs, and the same instance is used as the snapshot.
2222

2323
In cases where the built-in comparison behavior isn't appropriate, users may provide a *value comparer*, which contains logic for snapshotting, comparing and calculating a hash code. For example, the following sets up value conversion for `List<int>` property to be value converted to a JSON string in the database, and defines an appropriate value comparer as well:
2424

@@ -37,7 +37,7 @@ Consider byte arrays, which can be arbitrarily large. These could be compared:
3737
* By reference, such that a difference is only detected if a new byte array is used
3838
* By deep comparison, such that mutation of the bytes in the array is detected
3939

40-
By default, EF Core uses the first of these approaches for non-key byte arrays. That is, only references are compared and a change is detected only when an existing byte array is replaced with a new one. This is a pragmatic decision that avoids copying entire arrays and comparing them byte-to-byte when executing <xref:Microsoft.EntityFrameworkCore.DbContext.SaveChanges%2A>, and the common scenario of replacing, say, one image with another is handled in a performant way.
40+
By default, EF Core uses the first of these approaches for non-key byte arrays. That is, only references are compared and a change is detected only when an existing byte array is replaced with a new one. This is a pragmatic decision that avoids copying entire arrays and comparing them byte-to-byte when executing <xref:Microsoft.EntityFrameworkCore.DbContext.SaveChanges%2A>. It means that the common scenario of replacing, say, one image with another is handled in a performant way.
4141

4242
On the other hand, reference equality would not work when byte arrays are used to represent binary keys, since it's very unlikely that an FK property is set to the _same instance_ as a PK property to which it needs to be compared. Therefore, EF Core uses deep comparisons for byte arrays acting as keys; this is unlikely to have a big performance hit since binary keys are usually short.
4343

@@ -66,18 +66,11 @@ The mapping for simple structs is also simple and requires no special comparers
6666

6767
[!code-csharp[ConfigureImmutableStructProperty](../../../samples/core/Modeling/ValueConversions/MappingImmutableStructProperty.cs?name=ConfigureImmutableStructProperty)]
6868

69-
EF Core has built-in support for generating compiled, memberwise comparisons of struct properties.
70-
This means structs don't need to have equality overridden for EF Core, but you may still choose to do this for [other reasons](/dotnet/csharp/programming-guide/statements-expressions-operators/how-to-define-value-equality-for-a-type).
71-
Also, special snapshotting is not needed since structs are immutable and are always copied memberwise anyway.
72-
(This is also true for mutable structs, but [mutable structs should in general be avoided](/dotnet/csharp/write-safe-efficient-code).)
69+
EF Core has built-in support for generating compiled, memberwise comparisons of struct properties. This means structs don't need to have equality overridden for EF Core, but you may still choose to do this for [other reasons](/dotnet/csharp/programming-guide/statements-expressions-operators/how-to-define-value-equality-for-a-type). Also, special snapshotting is not needed since structs are immutable and are always copied memberwise anyway. (This is also true for mutable structs, but [mutable structs should in general be avoided](/dotnet/csharp/write-safe-efficient-code).)
7370

7471
## Mutable classes
7572

76-
It is recommended that you use immutable types (classes or structs) with value converters when possible.
77-
This is usually more efficient and has cleaner semantics than using a mutable type.
78-
79-
However, that being said, it is common to use properties of types that the application cannot change.
80-
For example, mapping a property containing a list of numbers:
73+
It is recommended that you use immutable types (classes or structs) with value converters when possible. This is usually more efficient and has cleaner semantics than using a mutable type. However, that being said, it is common to use properties of types that the application cannot change. For example, mapping a property containing a list of numbers:
8174

8275
[!code-csharp[ListProperty](../../../samples/core/Modeling/ValueConversions/MappingListProperty.cs?name=ListProperty)]
8376

@@ -106,24 +99,16 @@ The <xref:Microsoft.EntityFrameworkCore.ChangeTracking.ValueComparer%601> constr
10699

107100
In this case the comparison is done by checking if the sequences of numbers are the same.
108101

109-
Likewise, the hash code is built from this same sequence.
110-
(Note that this is a hash code over mutable values and hence can [cause problems](https://ericlippert.com/2011/02/28/guidelines-and-rules-for-gethashcode/).
111-
Be immutable instead if you can.)
102+
Likewise, the hash code is built from this same sequence. (Note that this is a hash code over mutable values and hence can [cause problems](https://ericlippert.com/2011/02/28/guidelines-and-rules-for-gethashcode/). Be immutable instead if you can.)
112103

113-
The snapshot is created by cloning the list with `ToList`.
114-
Again, this is only needed if the lists are going to be mutated.
115-
Be immutable instead if you can.
104+
The snapshot is created by cloning the list with `ToList`. Again, this is only needed if the lists are going to be mutated. Be immutable instead if you can.
116105

117106
> [!NOTE]
118-
> Value converters and comparers are constructed using expressions rather than simple delegates.
119-
> This is because EF Core inserts these expressions into a much more complex expression tree that is then compiled into an entity shaper delegate.
120-
> Conceptually, this is similar to compiler inlining.
121-
> For example, a simple conversion may just be a compiled in cast, rather than a call to another method to do the conversion.
107+
> Value converters and comparers are constructed using expressions rather than simple delegates. This is because EF Core inserts these expressions into a much more complex expression tree that is then compiled into an entity shaper delegate. Conceptually, this is similar to compiler inlining. For example, a simple conversion may just be a compiled in cast, rather than a call to another method to do the conversion.
122108
123109
## Key comparers
124110

125-
The background section covers why key comparisons may require special semantics.
126-
Make sure to create a comparer that is appropriate for keys when setting it on a primary, principal, or foreign key property.
111+
The background section covers why key comparisons may require special semantics. Make sure to create a comparer that is appropriate for keys when setting it on a primary, principal, or foreign key property.
127112

128113
Use <xref:Microsoft.EntityFrameworkCore.MutablePropertyExtensions.SetKeyValueComparer%2A> in the rare cases where different semantics is required on the same property.
129114

@@ -132,9 +117,7 @@ Use <xref:Microsoft.EntityFrameworkCore.MutablePropertyExtensions.SetKeyValueCom
132117
133118
## Overriding the default comparer
134119

135-
Sometimes the default comparison used by EF Core may not be appropriate.
136-
For example, mutation of byte arrays is not, by default, detected in EF Core.
137-
This can be overridden by setting a different comparer on the property:
120+
Sometimes the default comparison used by EF Core may not be appropriate. For example, mutation of byte arrays is not, by default, detected in EF Core. This can be overridden by setting a different comparer on the property:
138121

139122
[!code-csharp[OverrideComparer](../../../samples/core/Modeling/ValueConversions/OverridingByteArrayComparisons.cs?name=OverrideComparer)]
140123

0 commit comments

Comments
 (0)