diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj index dd1b7254168c78977bbef5f167774b9b911a6006..2a723d2c64bc58ea4e0a3d9aedfae9d090291aca 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -1,7 +1,7 @@ - 6.1.1-beta12 + 6.1.1 diff --git a/src/BootstrapBlazor/Components/Checkbox/CheckboxList.razor.cs b/src/BootstrapBlazor/Components/Checkbox/CheckboxList.razor.cs index 8ee0380318ebd097b4c4d9d75563e792b9c69682..38f7bde1b8c2438a7cbe2d3af92b2bd77a90fac4 100644 --- a/src/BootstrapBlazor/Components/Checkbox/CheckboxList.razor.cs +++ b/src/BootstrapBlazor/Components/Checkbox/CheckboxList.razor.cs @@ -85,8 +85,7 @@ namespace BootstrapBlazor.Components { var pi = FieldIdentifier.Value.Model.GetType() .GetProperties() - .Where(p => p.Name == FieldIdentifier.Value.FieldName) - .FirstOrDefault(); + .FirstOrDefault(p => p.Name == FieldIdentifier.Value.FieldName); if (pi != null) { var required = pi.GetCustomAttribute(true); diff --git a/src/BootstrapBlazor/Components/Transfer/Transfer.razor.cs b/src/BootstrapBlazor/Components/Transfer/Transfer.razor.cs index 2aa4be26c642dc9447eb010bfdeed0a55d97725b..35dffedfe82469b063bbc8d88e0ab6c85b1cdc02 100644 --- a/src/BootstrapBlazor/Components/Transfer/Transfer.razor.cs +++ b/src/BootstrapBlazor/Components/Transfer/Transfer.razor.cs @@ -139,8 +139,7 @@ namespace BootstrapBlazor.Components { var pi = FieldIdentifier.Value.Model.GetType() .GetProperties() - .Where(p => p.Name == FieldIdentifier.Value.FieldName) - .FirstOrDefault(); + .FirstOrDefault(p => p.Name == FieldIdentifier.Value.FieldName); if (pi != null) { var required = pi.GetCustomAttribute(true); diff --git a/src/BootstrapBlazor/Components/Validate/ValidateBase.cs b/src/BootstrapBlazor/Components/Validate/ValidateBase.cs index 3596e40b95b53377f3c40ae3bbf10ae54efb9803..15c2c2b12d92d119f03555cf16eb344d216b4f89 100644 --- a/src/BootstrapBlazor/Components/Validate/ValidateBase.cs +++ b/src/BootstrapBlazor/Components/Validate/ValidateBase.cs @@ -245,7 +245,8 @@ namespace BootstrapBlazor.Components protected virtual string? FormatParsingErrorMessage() => ParsingErrorMessage; private bool IsRequired() => FieldIdentifier?.Model.GetType() - .GetProperty(FieldIdentifier.Value.FieldName)?.GetCustomAttribute(true) != null + .GetProperties() + .FirstOrDefault(p => p.Name == FieldIdentifier.Value.FieldName)?.GetCustomAttribute(true) != null || (ValidateRules?.OfType().Select(i => i.Validator).OfType().Any() ?? false); /// diff --git a/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs b/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs index 11644697141360894f3650617710440b0fddba61..c2d5f514bb74c190ce15ddbcf0347674585e0eea 100644 --- a/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs +++ b/src/BootstrapBlazor/Components/ValidateForm/ValidateForm.razor.cs @@ -167,8 +167,7 @@ namespace BootstrapBlazor.Components modelType = modelTypeInfo; fieldName = propName; var propertyInfo = modelType.GetProperties() - .Where(p => p.Name == propName) - .FirstOrDefault(); + .FirstOrDefault(p => p.Name == propName); if (propertyInfo == null) { break; @@ -211,7 +210,7 @@ namespace BootstrapBlazor.Components if (validator.IsNeedValidate) { var messages = new List(); - var pi = key.ModelType.GetProperties().Where(p => p.Name == key.FieldName).FirstOrDefault(); + var pi = key.ModelType.GetProperties().FirstOrDefault(p => p.Name == key.FieldName); if (pi != null) { var propertyValidateContext = new ValidationContext(fieldIdentifier.Model) @@ -247,7 +246,7 @@ namespace BootstrapBlazor.Components if (validator.IsNeedValidate) { var fieldName = fieldIdentifier.FieldName; - var pi = fieldIdentifier.Model.GetType().GetProperties().Where(p => p.Name == fieldName).FirstOrDefault(); + var pi = fieldIdentifier.Model.GetType().GetProperties().FirstOrDefault(p => p.Name == fieldName); if (pi != null) { var propertyValue = Utility.GetPropertyValue(fieldIdentifier.Model, fieldIdentifier.FieldName); diff --git a/src/BootstrapBlazor/Utils/Utility.cs b/src/BootstrapBlazor/Utils/Utility.cs index 4ae5011749dff7c2cad92a5b179bb8ec50af83d6..744eff9b74b284faf73d4819691670cbf7cb8cdc 100644 --- a/src/BootstrapBlazor/Utils/Utility.cs +++ b/src/BootstrapBlazor/Utils/Utility.cs @@ -123,7 +123,7 @@ namespace BootstrapBlazor.Components var v = new TModel(); foreach (var pi in source.GetType().GetProperties().Where(p => p.CanWrite)) { - var pinfo = v.GetType().GetProperties().Where(p => p.Name == pi.Name).FirstOrDefault(); + var pinfo = v.GetType().GetProperties().FirstOrDefault(p => p.Name == pi.Name); if (pinfo != null) { pi.SetValue(source, pinfo.GetValue(v)); @@ -174,7 +174,7 @@ namespace BootstrapBlazor.Components if (p.CanWrite) { var v = p.GetValue(item); - var property = valType.GetProperty(p.Name); + var property = valType.GetProperties().FirstOrDefault(i => i.Name == p.Name && i.PropertyType == p.PropertyType); if (property != null) { property.SetValue(ret, v); @@ -348,7 +348,8 @@ namespace BootstrapBlazor.Components public static object GenerateValueExpression(object model, string fieldName, Type fieldType) { // ValueExpression - var body = Expression.Property(Expression.Constant(model), model.GetType(), fieldName); + var pi = model.GetType().GetProperties().FirstOrDefault(p => p.Name == fieldName) ?? throw new InvalidOperationException($"the model {model.GetType().Name} not found property {fieldName}"); + var body = Expression.Property(Expression.Constant(model), pi); var tDelegate = typeof(Func<>).MakeGenericType(fieldType); return Expression.Lambda(tDelegate, body); } diff --git a/test/UnitTest/Components/ValidateTest.cs b/test/UnitTest/Components/ValidateTest.cs index c51754adef8b0b08a7c29337b3719db97483069e..f84a6d17481e30c80f5a479bca560187ab0944ec 100644 --- a/test/UnitTest/Components/ValidateTest.cs +++ b/test/UnitTest/Components/ValidateTest.cs @@ -499,6 +499,28 @@ namespace UnitTest.Components }); } + [Fact] + public void Required_AmbiguousMatch() + { + var model = new Cat(); + var rules = new List + { + new FormItemValidator(new RequiredAttribute()) + }; + var cut = Context.RenderComponent(builder => + { + builder.Add(v => v.Model, model); + builder.AddChildContent>(pb => + { + pb.Add(v => v.Value, model.Foo); + pb.Add(v => v.ValueExpression, Utility.GenerateValueExpression(model, nameof(Cat.Foo), typeof(int))); + pb.Add(v => v.ValidateRules, rules); + }); + }); + + // 不会报错 AmbiguousMatchException + } + [Fact] public void TooltipHost_Ok() { @@ -532,5 +554,16 @@ namespace UnitTest.Components OnValidate(true); } } + + class Dummy + { + public virtual string? Foo { get; set; } + } + + class Cat : Dummy + { + [Required] + public new int Foo { get; set; } + } } } diff --git a/test/UnitTest/Utils/ReflectionTest.cs b/test/UnitTest/Utils/ReflectionTest.cs new file mode 100644 index 0000000000000000000000000000000000000000..fa879230bf4c49ba7813467f62f52a71c841c4be --- /dev/null +++ b/test/UnitTest/Utils/ReflectionTest.cs @@ -0,0 +1,60 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using System.Linq; +using System.Reflection; +using Xunit; + +namespace UnitTest.Utils; + +public class ReflectionTest +{ + [Fact] + public void SubClass_Override() + { + var dog = new Dog() { Foo = "Test" }; + Assert.Equal("Test", dog.Foo); + + var p1 = dog.GetType().GetProperty("Foo"); + var p = dog.GetType().GetProperties().FirstOrDefault(p => p.Name == "Foo"); + + // 两种获取属性实例相等 + Assert.Equal(p1, p); + + // 反射获取值 + var v = p!.GetValue(dog); + Assert.Equal("Test", v); + } + + [Fact] + public void SubClass_New() + { + var cat = new Cat() { Foo = 1 }; + Assert.Equal(1, cat.Foo); + + // 由于使用 new 关键字导致报错混淆异常 + Assert.ThrowsAny(() => cat.GetType().GetProperty("Foo")); + + var p = cat.GetType().GetProperties().FirstOrDefault(p => p.Name == "Foo"); + + // 反射获取值 + var v = p!.GetValue(cat); + Assert.Equal(1, v); + } + + private class Dummy + { + public virtual string? Foo { get; set; } + } + + private class Dog : Dummy + { + public override string? Foo { get; set; } + } + + private class Cat : Dummy + { + public new int Foo { get; set; } + } +}