|
| 1 | +--- |
| 2 | +title: Value Comparers - EF Core |
| 3 | +author: ajcvickers |
| 4 | +ms.date: 03/17/2020 |
| 5 | +uid: core/modeling/value-comparers |
| 6 | +--- |
| 7 | + |
| 8 | +# Value Comparers |
| 9 | + |
| 10 | +> [!NOTE] |
| 11 | +> This feature is new in EF Core 3.0. |
| 12 | +
|
| 13 | +> [!TIP] |
| 14 | +> The code in this document can be found on GitHub as a [runnable sample](https://github.com/dotnet/EntityFramework.Docs/tree/master/samples/core/Modeling/ValueConversions/). |
| 15 | +
|
| 16 | +## Background |
| 17 | + |
| 18 | +EF Core needs to compare property values when: |
| 19 | + |
| 20 | +* Determining whether or not a property has been changed as part of [detecting changes for updates](../saving/basic.md) |
| 21 | +* Determining whether or not two key values are the same when resolving relationships |
| 22 | + |
| 23 | +This is handled automatically for common primitive types with little internal structure, such as int, DateTime, etc. |
| 24 | + |
| 25 | +For more complex types, choices need to be made as to how to do the comparison. |
| 26 | +For example, a byte array could be compared: |
| 27 | + |
| 28 | +* By reference, such that a difference is only detected if a new byte array is used |
| 29 | +* By deep comparison, such that mutation of the bytes in the array is detected |
| 30 | + |
| 31 | +EF Core uses the first of these approaches for non-key byte arrays. |
| 32 | +That is, only references are compared and a change is detected when an existing byte array is replaced with a new one. |
| 33 | +This is a pragmatic decision that avoids deep comparison of many large byte arrays when executing SaveChanges. |
| 34 | +But the common scenario of replacing, say, an image with a different image is handled in a performant way. |
| 35 | + |
| 36 | +On the other hand, reference equality would not work when byte arrays are used to represent binary keys. |
| 37 | +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. |
| 38 | +Therefore, EF Core uses deep comparisons for byte arrays acting as keys. |
| 39 | +This is unlikely to have a big performance hit since binary keys are usually short. |
| 40 | + |
| 41 | +### Snapshots |
| 42 | + |
| 43 | +Deep comparisons on mutable types means that EF Core needs the ability to create a deep "snapshot" of the property value. |
| 44 | +Just copying the reference instead would result in mutating both the current value and the snapshot, since they are _the same object_. |
| 45 | +Therefore, when deep comparisons are used, deep snapshotting is also required. |
| 46 | + |
| 47 | +## Properties with value converters |
| 48 | + |
| 49 | +In the case above, EF Core has native mapping support for byte arrays and so can automatically choose appropriate defaults. |
| 50 | +However, if the property is mapped through a [value converter](value-conversions.md), then EF Core can't always determine the appropriate comparison to use. |
| 51 | +Instead, EF Core always uses the default equality comparison defined by the property type. |
| 52 | +This is often correct, but may need to be overridden when mapping more complex types. |
| 53 | + |
| 54 | +### Simple immutable classes |
| 55 | + |
| 56 | +Consider a property the uses a value converter to map a simple, immutable class. |
| 57 | + |
| 58 | +[!code-csharp[SimpleImmutableClass](../../../samples/core/Modeling/ValueConversions/Program.cs?name=SimpleImmutableClass)] |
| 59 | + |
| 60 | +[!code-csharp[ConfigureImmutableClassProperty](../../../samples/core/Modeling/ValueConversions/Program.cs?name=ConfigureImmutableClassProperty)] |
| 61 | + |
| 62 | +Properties of this type do not need special comparisons or snapshots because: |
| 63 | +* Equality is overridden so that different instances will compare correctly |
| 64 | +* The type is immutable, so there is no chance of mutating a snapshot value |
| 65 | + |
| 66 | +So in this case the value converted mapping is fine as it is. |
| 67 | + |
| 68 | +### Simple immutable Structs |
| 69 | + |
| 70 | +The mapping for simple structs is also simple and requires no special comparers or snapshotting. |
| 71 | + |
| 72 | +[!code-csharp[SimpleImmutableStruct](../../../samples/core/Modeling/ValueConversions/Program.cs?name=SimpleImmutableStruct)] |
| 73 | + |
| 74 | +[!code-csharp[ConfigureImmutableStructProperty](../../../samples/core/Modeling/ValueConversions/Program.cs?name=ConfigureImmutableStructProperty)] |
| 75 | + |
| 76 | +EF Core has built-in support for generating compiled, memberwise comparisons of struct properties. |
| 77 | +This means structs don't need to have equality overridden for EF, but you may still choose to do this for other reasons. |
| 78 | +Also, snapshotting is fine since structs are always memberwise copied. |
| 79 | +(This is also true for mutable structs, but [mutable structs should in general be avoided](https://docs.microsoft.com/dotnet/csharp/write-safe-efficient-code).) |
| 80 | + |
| 81 | +### Mutable classes |
| 82 | + |
| 83 | +It is recommended that you use immutable types (classes or structs) with value converters when possible. |
| 84 | +This is usually more efficient and has cleaner semantics than using a mutable type. |
| 85 | + |
| 86 | +However, that being said, it is common to use properties of types that the application cannot change. |
| 87 | +For example, mapping a property containing a list of numbers: |
| 88 | + |
| 89 | +[!code-csharp[ListProperty](../../../samples/core/Modeling/ValueConversions/Program.cs?name=ListProperty)] |
| 90 | + |
| 91 | +The [List class](https://docs.microsoft.com/dotnet/api/system.collections.generic.list-1?view=netstandard-2.1): |
| 92 | +* Has reference equality; two lists containing the same values are treated as different. |
| 93 | +* Is mutable; values in the list can be added, removed, and modified. |
| 94 | + |
| 95 | +A typical value conversion on a list property might convert the list to and from JSON: |
| 96 | + |
| 97 | +[!code-csharp[ConfigureListProperty](../../../samples/core/Modeling/ValueConversions/Program.cs?name=ConfigureListProperty)] |
| 98 | + |
| 99 | +Then, to get correct comparisons, create and set a `ValueComparer<T>` on the same property: |
| 100 | + |
| 101 | +[!code-csharp[ConfigureListPropertyComparer](../../../samples/core/Modeling/ValueConversions/Program.cs?name=ConfigureListPropertyComparer)] |
| 102 | + |
| 103 | +> [!NOTE] |
| 104 | +> The model builder ("fluent") API to set a value comparer has not yet been implemented. |
| 105 | +> Instead, the code above gets the lower-level `IMutableProperty` from the builder's `Metadata` property and sets the comparer directly. |
| 106 | +
|
| 107 | +The `ValueComparer<T>` constructor accepts three expressions: |
| 108 | +* An expression for checking quality |
| 109 | +* An expression for generating a hash code |
| 110 | +* An expression to snapshot a value |
| 111 | + |
| 112 | +In this case the comparision is done by checking if the sequences of numbers are the same. |
| 113 | + |
| 114 | +Likewise, the hash code is built from this same sequence. |
| 115 | +(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/). |
| 116 | +Be immutable instead if you can.) |
| 117 | + |
| 118 | +The snapshot is created by cloning the list with `ToList`. |
| 119 | +Again, this is only needed if the lists are going to be mutated. |
| 120 | +Be immutable instead if you can. |
| 121 | + |
| 122 | +> [!NOTE] |
| 123 | +> Value converters and comparers are constructed using expressions rather than simple delegates. |
| 124 | +> This is because EF inserts these expressions into a much more complex expression tree that is then compiled into an entity shaper delegate. |
| 125 | +> Conceptually, this is similar to compiler inlining. |
| 126 | +> For example, a simple conversion may just be a compiled in cast, rather than a call to another method to do the conversion. |
| 127 | +
|
| 128 | +### Key comparers |
| 129 | + |
| 130 | +The background section covered why key comparisons may require special semantics. |
| 131 | +A property is either part of a key property or it isn't. |
| 132 | +Make sure to create a comparer that is appropriate for keys when setting one for key properties. |
| 133 | + |
| 134 | +Use [SetKeyValueComparer](https://docs.microsoft.com/dotnet/api/microsoft.entityframeworkcore.mutablepropertyextensions.setkeyvaluecomparer?view=efcore-3.1) in the rare cases where different semantics is required on the same property. |
| 135 | + |
| 136 | +> [!NOTE] |
| 137 | +> SetStructuralComparer has been obsoleted in EF Core 5.0. |
| 138 | +> Use SetKeyValueComparer instead. |
| 139 | +
|
| 140 | +### Overriding defaults |
| 141 | + |
| 142 | +Sometimes the default comparison used by EF Core may not be appropriate. |
| 143 | +For example, mutation of byte arrays is not by default detected by EF Core. |
| 144 | +This can be overridden by setting a different comparer on the property: |
| 145 | + |
| 146 | +[!code-csharp[OverrideComparer](../../../samples/core/Modeling/ValueConversions/Program.cs?name=OverrideComparer)] |
0 commit comments