|
1 | | ---- |
2 | | -uid: Uno.Extensions.KeyEquality.Concept |
3 | | ---- |
4 | | -# Concept |
5 | | - |
6 | | -When working with immutable objects (usually `record`), each modification on an entity means that a new instance is created. |
7 | | -While this offers lots of [great advantages](https://en.wikipedia.org/wiki/Immutable_object), it makes more difficult to properly track (and animate) changes on an (immutable) item in a (immutable) collection. |
8 | | - |
9 | | -The _key equality_ is a standardized way to determine that 2 objects that are not `Equals` are however representing the same entity. |
10 | | - |
11 | | -For instance: |
12 | | - |
13 | | -```csharp |
14 | | -public partial record Person(string Name, int Age); |
15 | | - |
16 | | -var john1 = new Person("John Doe", 20); |
17 | | -var john2 = john1 with { Age=21 }; |
18 | | - |
19 | | -Console.WriteLine("Are the same : " + john1.Equals(john2)); |
20 | | -Console.WriteLine("Are the same person : " + john1.KeyEquals(john2)); |
21 | | -``` |
22 | | - |
23 | | -This would output: |
24 | | - |
25 | | -```output |
26 | | -Are the same : false |
27 | | -Are the same person : true |
28 | | -``` |
29 | | - |
30 | | -In this example, the _key_ is the `Name`, and any instance of `Person` that `Name == "John Doe"` is considered to represent the same person, |
31 | | -no matter values of the other properties. |
32 | | - |
33 | | -Then if you are dealing with list: |
34 | | - |
35 | | -```csharp |
36 | | -var list1 = ImmutableList.Create(john1); |
37 | | -var list2 = list1.Replace(john1, john2); |
38 | | -``` |
39 | | - |
40 | | -When comparing the `list1` and the `list2` the `IKeyEquatable<T>` allows you to detect that the item is only a newer version of the same entity, |
41 | | -so visually we only need to update the current item and not animate the removal and then the add of the item. |
42 | | - |
43 | | -The implementation of this concept is located in the `Uno.Extensions.Equality` namespace. |
44 | | - |
45 | | -## IKeyEquatable<T> |
46 | | - |
47 | | -This is like `IEquatable<T>` but specialized for the _key_ comparison. |
48 | | - |
49 | | -When you implement this, you should compare only the keys of your object. |
50 | | - |
51 | | -> [!TIP] |
52 | | -> You usually don't have to implement it by yourself, see [generation](#generation). |
53 | | -
|
54 | | -## KeyEqualityComparer |
55 | | - |
56 | | -This is like the `EqualityComparer` but which relies on the `IKeyEquatable` instead of `IEquatable` to check equality. |
57 | | - |
58 | | -> [!TIP] |
59 | | -> As all types are not necessarily implementing `IKeyEquatable<T>`, there is no equivalent to the `EqualityComparer<T>.Default`. |
60 | | -> You can however use the static `KeyEqualityComparer.Find<T>()` to dynamically get a _key equality_ comparer, |
61 | | -> if the given `T` does implement `IKeyEquatable<T>`. |
62 | | -
|
63 | | -## Generation |
64 | | - |
65 | | -The `IKeyEquatable<T>` implementation is automatically generated for `partial record` that has a property named `Id` or `Key`. |
66 | | - |
67 | | -### How to configure keys? |
68 | | - |
69 | | -You have several way to configure the keys: |
70 | | - |
71 | | -1. Add the `[Key]` attribute on properties that should be used for _key equality_. |
72 | | - |
73 | | - ```csharp |
74 | | - public partial record MyItem( |
75 | | - [property:Key] Guid EntityId, |
76 | | - [property:Key] string SourceId, |
77 | | - string Value); |
78 | | - ``` |
79 | | - |
80 | | - > [!IMPORTANT] |
81 | | - > As soon as a property has been flagged with the `[Key]` attribute, the implicit keys are not used. |
82 | | - > |
83 | | - > [!NOTE] |
84 | | - > This is the single way to have more than one property to compute _key equality_. |
85 | | - > |
86 | | - > [!NOTE] |
87 | | - > You can use indifferently the `Uno.Extensions.Equality.KeyAttribute` or the `System.ComponentModel.DataAnnotations.KeyAttribute` |
88 | | - |
89 | | -2. Add the `[ImplicitKeyEquality]` attribute on your record |
90 | | - |
91 | | - ```csharp |
92 | | - [ImplicitKeyEquality("EntityId")] |
93 | | - public partial record MyItem(Guid EntityId, string SourceId, string Value); |
94 | | - ``` |
95 | | - |
96 | | -3. Change the default keys for the whole project by setting the `[ImplicitKeyEquality]` on the assembly: |
97 | | - |
98 | | - ```csharp |
99 | | - [assembly:ImplicitKeyEquality("Id", "Key", "EntityId")] |
100 | | - ``` |
101 | | - |
102 | | - > [!IMPORTANT] |
103 | | - > The generation of `IKeyEquatable<T>` using implicit keys will use only **one** matching property. |
104 | | - > The properties a tested in the order in which they have been defined on the `[ImplicitKeyEquality]` attribute. |
105 | | - > This means that in the example above, if a record have 2 properties `Id` and `EntityId`, only the property named `Id` will be used. |
106 | | - |
107 | | -## How to disable generation? |
108 | | - |
109 | | -You can disable the generation on a given type by adding `[ImplicitKeys(IsEnabled = false)]` on it. |
110 | | - |
111 | | -To disable the generation for the whole project, set that attribute directly on the assembly: |
112 | | - |
113 | | -```csharp |
114 | | -[assembly:ImplicitKeys(IsEnabled = false)] |
115 | | -``` |
116 | | - |
117 | | -> [!IMPORTANT] |
118 | | -> This disable only the generation based on implicit keys. |
119 | | -> If you have a record that has a property flagged with the `[Key]` attribute, |
120 | | -> the generator will still generate the `IKeyEquatable<T>` implementation for that type. |
| 1 | +--- |
| 2 | +uid: Uno.Extensions.KeyEquality.Concept |
| 3 | +--- |
| 4 | +# Concept |
| 5 | + |
| 6 | +When working with immutable objects (usually `record`), each modification on an entity means that a new instance is created. |
| 7 | +While this offers lots of [great advantages](https://en.wikipedia.org/wiki/Immutable_object), it makes more difficult to properly track (and animate) changes on an (immutable) item in a (immutable) collection. |
| 8 | + |
| 9 | +The _key equality_ is a standardized way to determine that 2 objects that are not `Equals` are however representing the same entity. |
| 10 | + |
| 11 | +For instance: |
| 12 | + |
| 13 | +```csharp |
| 14 | +public partial record Person(string Name, int Age); |
| 15 | + |
| 16 | +var john1 = new Person("John Doe", 20); |
| 17 | +var john2 = john1 with { Age=21 }; |
| 18 | + |
| 19 | +Console.WriteLine("Are the same : " + john1.Equals(john2)); |
| 20 | +Console.WriteLine("Are the same person : " + john1.KeyEquals(john2)); |
| 21 | +``` |
| 22 | + |
| 23 | +This would output: |
| 24 | + |
| 25 | +```output |
| 26 | +Are the same : false |
| 27 | +Are the same person : true |
| 28 | +``` |
| 29 | + |
| 30 | +In this example, the _key_ is the `Name`, and any instance of `Person` that `Name == "John Doe"` is considered to represent the same person, |
| 31 | +no matter values of the other properties. |
| 32 | + |
| 33 | +Then if you are dealing with list: |
| 34 | + |
| 35 | +```csharp |
| 36 | +var list1 = ImmutableList.Create(john1); |
| 37 | +var list2 = list1.Replace(john1, john2); |
| 38 | +``` |
| 39 | + |
| 40 | +When comparing the `list1` and the `list2` the `IKeyEquatable<T>` allows you to detect that the item is only a newer version of the same entity, |
| 41 | +so visually we only need to update the current item and not animate the removal and then the add of the item. |
| 42 | + |
| 43 | +The implementation of this concept is located in the `Uno.Extensions.Equality` namespace. |
| 44 | + |
| 45 | +## `IKeyEquatable<T>` |
| 46 | + |
| 47 | +This is like `IEquatable<T>` but specialized for the _key_ comparison. |
| 48 | + |
| 49 | +When you implement this, you should compare only the keys of your object. |
| 50 | + |
| 51 | +> [!TIP] |
| 52 | +> If you work with records you usually don't have to implement it by yourself, see [generation](#generation). |
| 53 | +
|
| 54 | +## KeyEqualityComparer |
| 55 | + |
| 56 | +This is like the `EqualityComparer` but which relies on the `IKeyEquatable` instead of `IEquatable` to check equality. |
| 57 | + |
| 58 | +> [!TIP] |
| 59 | +> As all types are not necessarily implementing `IKeyEquatable<T>`, there is no equivalent to the `EqualityComparer<T>.Default`. |
| 60 | +> You can however use the static `KeyEqualityComparer.Find<T>()` to dynamically get a _key equality_ comparer, |
| 61 | +> if the given `T` does implement `IKeyEquatable<T>`. |
| 62 | +
|
| 63 | +## Generation |
| 64 | + |
| 65 | +The `IKeyEquatable<T>` implementation is automatically generated for `partial record` that has a property named `Id` or `Key`. |
| 66 | + |
| 67 | +### How to configure keys? |
| 68 | + |
| 69 | +You have several way to configure the keys: |
| 70 | + |
| 71 | +1. Add the `[Key]` attribute on properties that should be used for _key equality_. |
| 72 | + |
| 73 | + ```csharp |
| 74 | + public partial record MyItem( |
| 75 | + [property:Key] Guid EntityId, |
| 76 | + [property:Key] string SourceId, |
| 77 | + string Value); |
| 78 | + ``` |
| 79 | + |
| 80 | + > [!IMPORTANT] |
| 81 | + > As soon as a property has been flagged with the `[Key]` attribute, the implicit keys are not used. |
| 82 | + > |
| 83 | + > [!NOTE] |
| 84 | + > This is the single way to have more than one property to compute _key equality_. |
| 85 | + > |
| 86 | + > [!NOTE] |
| 87 | + > You can use indifferently the `Uno.Extensions.Equality.KeyAttribute` or the `System.ComponentModel.DataAnnotations.KeyAttribute` |
| 88 | + |
| 89 | +2. Add the `[ImplicitKeyEquality]` attribute on your record |
| 90 | + |
| 91 | + ```csharp |
| 92 | + [ImplicitKeyEquality("EntityId")] |
| 93 | + public partial record MyItem(Guid EntityId, string SourceId, string Value); |
| 94 | + ``` |
| 95 | + |
| 96 | +3. Change the default keys for the whole project by setting the `[ImplicitKeyEquality]` on the assembly: |
| 97 | + |
| 98 | + ```csharp |
| 99 | + [assembly:ImplicitKeyEquality("Id", "Key", "EntityId")] |
| 100 | + ``` |
| 101 | + |
| 102 | + > [!IMPORTANT] |
| 103 | + > The generation of `IKeyEquatable<T>` using implicit keys will use only **one** matching property. |
| 104 | + > The properties a tested in the order in which they have been defined on the `[ImplicitKeyEquality]` attribute. |
| 105 | + > This means that in the example above, if a record have 2 properties `Id` and `EntityId`, only the property named `Id` will be used. |
| 106 | + |
| 107 | +## How to disable generation? |
| 108 | + |
| 109 | +You can disable the generation on a given type by adding `[ImplicitKeys(IsEnabled = false)]` on it. |
| 110 | + |
| 111 | +To disable the generation for the whole project, set that attribute directly on the assembly: |
| 112 | + |
| 113 | +```csharp |
| 114 | +[assembly:ImplicitKeys(IsEnabled = false)] |
| 115 | +``` |
| 116 | + |
| 117 | +> [!IMPORTANT] |
| 118 | +> This disable only the generation based on implicit keys. |
| 119 | +> If you have a record that has a property flagged with the `[Key]` attribute, |
| 120 | +> the generator will still generate the `IKeyEquatable<T>` implementation for that type. |
0 commit comments