Skip to content

Commit 54af995

Browse files
RemiBouSteveSandersonMS
authored andcommitted
Change AssignToProperties to clear all unset bindables
1 parent 6d2f248 commit 54af995

File tree

11 files changed

+52
-39
lines changed

11 files changed

+52
-39
lines changed

src/Components/src/Microsoft.AspNetCore.Components/CascadingValue.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public void Init(RenderHandle renderHandle)
5858
public void SetParameters(ParameterCollection parameters)
5959
{
6060
// Implementing the parameter binding manually, instead of just calling
61-
// parameters.AssignToProperties(this), is just a very slight perf optimization
61+
// parameters.SetParameterProperties(this), is just a very slight perf optimization
6262
// and makes it simpler impose rules about the params being required or not.
6363

6464
var hasSuppliedValue = false;

src/Components/src/Microsoft.AspNetCore.Components/ComponentBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ void IComponent.Init(RenderHandle renderHandle)
151151
/// <param name="parameters">The parameters to apply.</param>
152152
public virtual void SetParameters(ParameterCollection parameters)
153153
{
154-
parameters.AssignToProperties(this);
154+
parameters.SetParameterProperties(this);
155155

156156
if (!_hasCalledInit)
157157
{

src/Components/src/Microsoft.AspNetCore.Components/Layouts/LayoutDisplay.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
@@ -42,7 +42,7 @@ public void Init(RenderHandle renderHandle)
4242
/// <inheritdoc />
4343
public void SetParameters(ParameterCollection parameters)
4444
{
45-
parameters.AssignToProperties(this);
45+
parameters.SetParameterProperties(this);
4646
Render();
4747
}
4848

src/Components/src/Microsoft.AspNetCore.Components/ParameterCollectionExtensions.cs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System;
66
using System.Collections.Concurrent;
77
using System.Collections.Generic;
8+
using System.Linq;
89
using System.Reflection;
910

1011
namespace Microsoft.AspNetCore.Components
@@ -16,18 +17,15 @@ public static class ParameterCollectionExtensions
1617
{
1718
private const BindingFlags _bindablePropertyFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase;
1819

19-
private delegate void WriteParameterAction(object target, object parameterValue);
20-
21-
private readonly static IDictionary<Type, IDictionary<string, WriteParameterAction>> _cachedParameterWriters
22-
= new ConcurrentDictionary<Type, IDictionary<string, WriteParameterAction>>();
20+
private readonly static IDictionary<Type, IDictionary<string, IPropertySetter>> _cachedParameterWriters = new ConcurrentDictionary<Type, IDictionary<string, IPropertySetter>>();
2321

2422
/// <summary>
2523
/// Iterates through the <see cref="ParameterCollection"/>, assigning each parameter
2624
/// to a property of the same name on <paramref name="target"/>.
2725
/// </summary>
2826
/// <param name="parameterCollection">The <see cref="ParameterCollection"/>.</param>
2927
/// <param name="target">An object that has a public writable property matching each parameter's name and type.</param>
30-
public static void AssignToProperties(
28+
public static void SetParameterProperties(
3129
in this ParameterCollection parameterCollection,
3230
object target)
3331
{
@@ -43,6 +41,8 @@ public static void AssignToProperties(
4341
_cachedParameterWriters[targetType] = parameterWriters;
4442
}
4543

44+
var localParameterWriter = parameterWriters.Values.ToList();
45+
4646
foreach (var parameter in parameterCollection)
4747
{
4848
var parameterName = parameter.Name;
@@ -53,7 +53,8 @@ public static void AssignToProperties(
5353

5454
try
5555
{
56-
parameterWriter(target, parameter.Value);
56+
parameterWriter.SetValue(target, parameter.Value);
57+
localParameterWriter.Remove(parameterWriter);
5758
}
5859
catch (Exception ex)
5960
{
@@ -62,14 +63,19 @@ public static void AssignToProperties(
6263
$"type '{target.GetType().FullName}'. The error was: {ex.Message}", ex);
6364
}
6465
}
66+
67+
foreach (var nonUsedParameter in localParameterWriter)
68+
{
69+
nonUsedParameter.SetValue(target, nonUsedParameter.GetDefaultValue());
70+
}
6571
}
6672

6773
internal static IEnumerable<PropertyInfo> GetCandidateBindableProperties(Type targetType)
6874
=> MemberAssignment.GetPropertiesIncludingInherited(targetType, _bindablePropertyFlags);
6975

70-
private static IDictionary<string, WriteParameterAction> CreateParameterWriters(Type targetType)
76+
private static IDictionary<string, IPropertySetter> CreateParameterWriters(Type targetType)
7177
{
72-
var result = new Dictionary<string, WriteParameterAction>(StringComparer.OrdinalIgnoreCase);
78+
var result = new Dictionary<string, IPropertySetter>(StringComparer.OrdinalIgnoreCase);
7379

7480
foreach (var propertyInfo in GetCandidateBindableProperties(targetType))
7581
{
@@ -90,10 +96,7 @@ private static IDictionary<string, WriteParameterAction> CreateParameterWriters(
9096
$"name '{propertyName.ToLowerInvariant()}'. Parameter names are case-insensitive and must be unique.");
9197
}
9298

93-
result.Add(propertyName, (object target, object parameterValue) =>
94-
{
95-
propertySetter.SetValue(target, parameterValue);
96-
});
99+
result.Add(propertyName, propertySetter);
97100
}
98101

99102
return result;

src/Components/src/Microsoft.AspNetCore.Components/Reflection/IPropertySetter.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
@@ -8,5 +8,7 @@ namespace Microsoft.AspNetCore.Components.Reflection
88
internal interface IPropertySetter
99
{
1010
void SetValue(object target, object value);
11+
12+
object GetDefaultValue();
1113
}
1214
}

src/Components/src/Microsoft.AspNetCore.Components/Reflection/MemberAssignment.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
@@ -43,15 +43,23 @@ public static IPropertySetter CreatePropertySetter(Type targetType, PropertyInfo
4343
class PropertySetter<TTarget, TValue> : IPropertySetter
4444
{
4545
private readonly Action<TTarget, TValue> _setterDelegate;
46+
private object _defaultValue;
4647

4748
public PropertySetter(MethodInfo setMethod)
4849
{
4950
_setterDelegate = (Action<TTarget, TValue>)Delegate.CreateDelegate(
5051
typeof(Action<TTarget, TValue>), setMethod);
52+
var propertyType = typeof(TValue);
53+
_defaultValue = propertyType.IsValueType ? Activator.CreateInstance(propertyType) : null;
5154
}
5255

5356
public void SetValue(object target, object value)
5457
=> _setterDelegate((TTarget)target, (TValue)value);
58+
59+
public object GetDefaultValue()
60+
{
61+
return _defaultValue;
62+
}
5563
}
5664
}
5765
}

src/Components/src/Microsoft.AspNetCore.Components/Routing/Router.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public void Init(RenderHandle renderHandle)
4545
/// <inheritdoc />
4646
public void SetParameters(ParameterCollection parameters)
4747
{
48-
parameters.AssignToProperties(this);
48+
parameters.SetParameterProperties(this);
4949
var types = ComponentResolver.ResolveComponents(AppAssembly);
5050
Routes = RouteTable.Create(types);
5151
Refresh();

src/Components/test/Microsoft.AspNetCore.Components.Test/ParameterCollectionAssignmentExtensionsTest.cs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public void IncomingParameterMatchesAnnotatedPrivateProperty_SetsValue()
2727
var target = new HasInstanceProperties();
2828

2929
// Act
30-
parameterCollection.AssignToProperties(target);
30+
parameterCollection.SetParameterProperties(target);
3131

3232
// Assert
3333
Assert.Equal(123, target.IntProp);
@@ -46,7 +46,7 @@ public void IncomingParameterMatchesDeclaredParameterCaseInsensitively_SetsValue
4646
var target = new HasInstanceProperties();
4747

4848
// Act
49-
parameterCollection.AssignToProperties(target);
49+
parameterCollection.SetParameterProperties(target);
5050

5151
// Assert
5252
Assert.Equal(123, target.IntProp);
@@ -64,15 +64,15 @@ public void IncomingParameterMatchesInheritedDeclaredParameter_SetsValue()
6464
var target = new HasInheritedProperties();
6565

6666
// Act
67-
parameterCollection.AssignToProperties(target);
67+
parameterCollection.SetParameterProperties(target);
6868

6969
// Assert
7070
Assert.Equal(123, target.IntProp);
7171
Assert.Equal(456, target.DerivedClassIntProp);
7272
}
7373

7474
[Fact]
75-
public void NoIncomingParameterMatchesDeclaredParameter_LeavesValueUnchanged()
75+
public void NoIncomingParameterMatchesDeclaredParameter_SetValuesDefault()
7676
{
7777
// Arrange
7878
var existingObjectValue = new object();
@@ -86,12 +86,12 @@ public void NoIncomingParameterMatchesDeclaredParameter_LeavesValueUnchanged()
8686
var parameterCollection = new ParameterCollectionBuilder().Build();
8787

8888
// Act
89-
parameterCollection.AssignToProperties(target);
89+
parameterCollection.SetParameterProperties(target);
9090

9191
// Assert
92-
Assert.Equal(456, target.IntProp);
93-
Assert.Equal("Existing value", target.StringProp);
94-
Assert.Same(existingObjectValue, target.ObjectPropCurrentValue);
92+
Assert.Equal(0, target.IntProp);
93+
Assert.Null(target.StringProp);
94+
Assert.Null(target.ObjectPropCurrentValue);
9595
}
9696

9797
[Fact]
@@ -106,7 +106,7 @@ public void IncomingParameterMatchesNoDeclaredParameter_Throws()
106106

107107
// Act
108108
var ex = Assert.Throws<InvalidOperationException>(
109-
() => parameterCollection.AssignToProperties(target));
109+
() => parameterCollection.SetParameterProperties(target));
110110

111111
// Assert
112112
Assert.Equal(
@@ -127,7 +127,7 @@ public void IncomingParameterMatchesPropertyNotDeclaredAsParameter_Throws()
127127

128128
// Act
129129
var ex = Assert.Throws<InvalidOperationException>(
130-
() => parameterCollection.AssignToProperties(target));
130+
() => parameterCollection.SetParameterProperties(target));
131131

132132
// Assert
133133
Assert.Equal(default, target.IntProp);
@@ -150,7 +150,7 @@ public void IncomingParameterValueMismatchesDeclaredParameterType_Throws()
150150

151151
// Act
152152
var ex = Assert.Throws<InvalidOperationException>(
153-
() => parameterCollection.AssignToProperties(target));
153+
() => parameterCollection.SetParameterProperties(target));
154154

155155
// Assert
156156
Assert.Equal(
@@ -171,7 +171,7 @@ public void PropertyExplicitSetterException_Throws()
171171

172172
// Act
173173
var ex = Assert.Throws<InvalidOperationException>(
174-
() => parameterCollection.AssignToProperties(target));
174+
() => parameterCollection.SetParameterProperties(target));
175175

176176
// Assert
177177
Assert.Equal(
@@ -189,7 +189,7 @@ public void DeclaredParametersVaryOnlyByCase_Throws()
189189

190190
// Act
191191
var ex = Assert.Throws<InvalidOperationException>(() =>
192-
parameterCollection.AssignToProperties(target));
192+
parameterCollection.SetParameterProperties(target));
193193

194194
// Assert
195195
Assert.Equal(
@@ -205,14 +205,14 @@ public void DeclaredParameterClashesWithInheritedParameter_Throws()
205205
// an allowed scenario because there would be no way for the consumer to specify
206206
// both property values, and it's no good leaving the shadowed one unset because the
207207
// base class can legitimately depend on it for correct functioning.
208-
208+
209209
// Arrange
210210
var parameterCollection = new ParameterCollectionBuilder().Build();
211211
var target = new HasParameterClashingWithInherited();
212212

213213
// Act
214214
var ex = Assert.Throws<InvalidOperationException>(() =>
215-
parameterCollection.AssignToProperties(target));
215+
parameterCollection.SetParameterProperties(target));
216216

217217
// Assert
218218
Assert.Equal(
@@ -226,7 +226,7 @@ class HasInstanceProperties
226226
{
227227
// "internal" to show we're not requiring public accessors, but also
228228
// to keep the assertions simple in the tests
229-
229+
230230
[Parameter] internal int IntProp { get; set; }
231231
[Parameter] internal string StringProp { get; set; }
232232

src/Components/test/Microsoft.AspNetCore.Components.Test/RenderTreeDiffBuilderTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1557,7 +1557,7 @@ private class FakeComponent : IComponent
15571557
public void Init(RenderHandle renderHandle) { }
15581558
public void SetParameters(ParameterCollection parameters)
15591559
{
1560-
parameters.AssignToProperties(this);
1560+
parameters.SetParameterProperties(this);
15611561
}
15621562
}
15631563

src/Components/test/Microsoft.AspNetCore.Components.Test/RendererTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,7 +1202,7 @@ public void Init(RenderHandle renderHandle)
12021202
=> RenderHandle = renderHandle;
12031203

12041204
public void SetParameters(ParameterCollection parameters)
1205-
=> parameters.AssignToProperties(this);
1205+
=> parameters.SetParameterProperties(this);
12061206
}
12071207

12081208
private class EventComponent : AutoRenderComponent, IComponent, IHandleEvent
@@ -1310,7 +1310,7 @@ public void Init(RenderHandle renderHandle)
13101310

13111311
public void SetParameters(ParameterCollection parameters)
13121312
{
1313-
parameters.AssignToProperties(this);
1313+
parameters.SetParameterProperties(this);
13141314
Render();
13151315
}
13161316

src/Components/test/shared/AutoRenderComponent.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public void Init(RenderHandle renderHandle)
1717

1818
public virtual void SetParameters(ParameterCollection parameters)
1919
{
20-
parameters.AssignToProperties(this);
20+
parameters.SetParameterProperties(this);
2121
TriggerRender();
2222
}
2323

0 commit comments

Comments
 (0)