Skip to content

Commit aad6550

Browse files
[release/8.0] [Blazor] Pass the member info directly (#53892)
* Pass the member info directly * Add additional test to cover non-ascii characters --------- Co-authored-by: jacalvar <[email protected]>
1 parent f734954 commit aad6550

File tree

2 files changed

+79
-6
lines changed

2 files changed

+79
-6
lines changed

src/Components/Forms/src/FieldIdentifier.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Collections.Concurrent;
55
using System.Diagnostics.CodeAnalysis;
66
using System.Linq.Expressions;
7+
using System.Reflection;
78
using System.Runtime.CompilerServices;
89
using Microsoft.AspNetCore.Components.HotReload;
910

@@ -15,7 +16,7 @@ namespace Microsoft.AspNetCore.Components.Forms;
1516
/// </summary>
1617
public readonly struct FieldIdentifier : IEquatable<FieldIdentifier>
1718
{
18-
private static readonly ConcurrentDictionary<(Type ModelType, string FieldName), Func<object, object>> _fieldAccessors = new();
19+
private static readonly ConcurrentDictionary<(Type ModelType, MemberInfo Member), Func<object, object>> _fieldAccessors = new();
1920

2021
static FieldIdentifier()
2122
{
@@ -151,7 +152,7 @@ private static void ParseAccessor<T>(Expression<Func<T>> accessor, out object mo
151152

152153
internal static object GetModelFromMemberAccess(
153154
MemberExpression member,
154-
ConcurrentDictionary<(Type ModelType, string FieldName), Func<object, object>>? cache = null)
155+
ConcurrentDictionary<(Type ModelType, MemberInfo Member), Func<object, object>>? cache = null)
155156
{
156157
cache ??= _fieldAccessors;
157158
Func<object, object>? accessor = null;
@@ -160,7 +161,7 @@ internal static object GetModelFromMemberAccess(
160161
{
161162
case ConstantExpression model:
162163
value = model.Value ?? throw new ArgumentException("The provided expression must evaluate to a non-null value.");
163-
accessor = cache.GetOrAdd((value.GetType(), member.Member.Name), CreateAccessor);
164+
accessor = cache.GetOrAdd((value.GetType(), member.Member), CreateAccessor);
164165
break;
165166
default:
166167
break;
@@ -183,11 +184,12 @@ internal static object GetModelFromMemberAccess(
183184
"Trimming",
184185
"IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code",
185186
Justification = "Application code does not get trimmed. We expect the members in the expression to not be trimmed.")]
186-
static Func<object, object> CreateAccessor((Type model, string member) arg)
187+
static Func<object, object> CreateAccessor((Type model, MemberInfo member) arg)
187188
{
188189
var parameter = Expression.Parameter(typeof(object), "value");
189190
Expression expression = Expression.Convert(parameter, arg.model);
190-
expression = Expression.PropertyOrField(expression, arg.member);
191+
192+
expression = Expression.MakeMemberAccess(expression, arg.member);
191193
expression = Expression.Convert(expression, typeof(object));
192194
var lambda = Expression.Lambda<Func<object, object>>(expression, parameter);
193195

src/Components/Forms/test/FieldIdentifierTest.cs

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Collections.Concurrent;
55
using System.Linq.Expressions;
6+
using System.Reflection;
67

78
namespace Microsoft.AspNetCore.Components.Forms;
89

@@ -142,7 +143,7 @@ public void CanCreateFromExpression_Property()
142143
public void CanCreateFromExpression_PropertyUsesCache()
143144
{
144145
var models = new TestModel[] { new TestModel(), new TestModel() };
145-
var cache = new ConcurrentDictionary<(Type ModelType, string FieldName), Func<object, object>>();
146+
var cache = new ConcurrentDictionary<(Type ModelType, MemberInfo FieldName), Func<object, object>>();
146147
var result = new TestModel[2];
147148
for (var i = 0; i < models.Length; i++)
148149
{
@@ -221,6 +222,53 @@ public void CanCreateFromExpression_MemberOfObjectWithCast()
221222
Assert.Equal(nameof(TestModel.StringField), fieldIdentifier.FieldName);
222223
}
223224

225+
[Fact]
226+
public void CanCreateFromExpression_DifferentCaseField()
227+
{
228+
var fieldIdentifier = FieldIdentifier.Create(() => model.Field);
229+
Assert.Same(model, fieldIdentifier.Model);
230+
Assert.Equal(nameof(model.Field), fieldIdentifier.FieldName);
231+
}
232+
233+
private DifferentCaseFieldModel model = new() { Field = 1 };
234+
#pragma warning disable CA1823 // This is used in the test above
235+
private DifferentCaseFieldModel Model = new() { field = 2 };
236+
#pragma warning restore CA1823 // Avoid unused private fields
237+
238+
[Fact]
239+
public void CanCreateFromExpression_DifferentCaseProperty()
240+
{
241+
var fieldIdentifier = FieldIdentifier.Create(() => Model2.Property);
242+
Assert.Same(Model2, fieldIdentifier.Model);
243+
Assert.Equal(nameof(Model2.Property), fieldIdentifier.FieldName);
244+
}
245+
246+
protected DifferentCasePropertyModel Model2 { get; } = new() { property = 1 };
247+
248+
protected DifferentCasePropertyModel model2 { get; } = new() { Property = 2 };
249+
250+
[Fact]
251+
public void CanCreateFromExpression_DifferentCasePropertyAndField()
252+
{
253+
var fieldIdentifier = FieldIdentifier.Create(() => model3.Value);
254+
Assert.Same(model3, fieldIdentifier.Model);
255+
Assert.Equal(nameof(Model3.Value), fieldIdentifier.FieldName);
256+
}
257+
258+
[Fact]
259+
public void CanCreateFromExpression_NonAsciiCharacters()
260+
{
261+
var fieldIdentifier = FieldIdentifier.Create(() => @ÖvrigAnställning.Ort);
262+
Assert.Same(@ÖvrigAnställning, fieldIdentifier.Model);
263+
Assert.Equal(nameof(@ÖvrigAnställning.Ort), fieldIdentifier.FieldName);
264+
}
265+
266+
public DifferentCasePropertyFieldModel Model3 { get; } = new() { value = 1 };
267+
268+
public DifferentCasePropertyFieldModel model3 = new() { Value = 2 };
269+
270+
public ÖvrigAnställningModel @ÖvrigAnställning { get; set; } = new();
271+
224272
string StringPropertyOnThisClass { get; set; }
225273

226274
class TestModel
@@ -253,4 +301,27 @@ public override int GetHashCode()
253301
return StringComparer.Ordinal.GetHashCode(Property);
254302
}
255303
}
304+
305+
public class ÖvrigAnställningModel
306+
{
307+
public int Ort { get; set; }
308+
}
309+
310+
private class DifferentCaseFieldModel
311+
{
312+
public int Field;
313+
public int field;
314+
}
315+
316+
protected class DifferentCasePropertyModel
317+
{
318+
public int Property { get; set; }
319+
public int property { get; set; }
320+
}
321+
322+
public class DifferentCasePropertyFieldModel
323+
{
324+
public int Value { get; set; }
325+
public int value;
326+
}
256327
}

0 commit comments

Comments
 (0)