diff --git a/src/BootstrapBlazor/Extensions/ObjectExtensions.cs b/src/BootstrapBlazor/Extensions/ObjectExtensions.cs
index 76864758ebcd69b8c9fa5d05cf9029321697fa64..6902ee8a10c31f06b1758041d483194bb5752fe4 100644
--- a/src/BootstrapBlazor/Extensions/ObjectExtensions.cs
+++ b/src/BootstrapBlazor/Extensions/ObjectExtensions.cs
@@ -113,7 +113,7 @@ public static class ObjectExtensions
else
{
var methodInfo = typeof(ObjectExtensions).GetMethods().FirstOrDefault(m => m.IsGenericMethod)!.MakeGenericMethod(type);
- var v = type == typeof(string) ? null : Activator.CreateInstance(type);
+ var v = Activator.CreateInstance(type);
var args = new object?[] { source, v };
ret = (bool)methodInfo.Invoke(null, args)!;
val = ret ? args[1] : null;
@@ -170,7 +170,7 @@ public static class ObjectExtensions
///
///
///
- internal static string ToFileSizeString(this long fileSize) => fileSize switch
+ public static string ToFileSizeString(this long fileSize) => fileSize switch
{
>= 1024 and < 1024 * 1024 => $"{Math.Round(fileSize / 1024D, 0, MidpointRounding.AwayFromZero)} KB",
>= 1024 * 1024 and < 1024 * 1024 * 1024 => $"{Math.Round(fileSize / 1024 / 1024D, 0, MidpointRounding.AwayFromZero)} MB",
@@ -202,14 +202,13 @@ public static class ObjectExtensions
{
var fieldName = col.GetFieldName();
var canWrite = IsDynamicObject();
- return canWrite || (fieldName.Contains('.')
- ? modelType.GetPropertyByName(fieldName)?.CanWrite ?? false
- : ComplexCanWrite());
+ return canWrite || ComplexCanWrite();
bool IsDynamicObject() => modelType == typeof(DynamicObject);
bool ComplexCanWrite()
{
+ var ret = false;
var propertyNames = fieldName.Split('.');
PropertyInfo? propertyInfo = null;
Type? propertyType = null;
@@ -226,7 +225,11 @@ public static class ObjectExtensions
propertyType = propertyInfo.PropertyType;
}
}
- return propertyInfo?.CanWrite ?? false;
+ if (propertyInfo != null)
+ {
+ ret = propertyInfo.CanWrite;
+ }
+ return ret;
}
}
}
diff --git a/test/UnitTest/Extensions/ObjectExtensionsTest.cs b/test/UnitTest/Extensions/ObjectExtensionsTest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b7eb4c395d0cd635395956ed7fc46ae33bce6d3d
--- /dev/null
+++ b/test/UnitTest/Extensions/ObjectExtensionsTest.cs
@@ -0,0 +1,262 @@
+// 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 BootstrapBlazor.Shared;
+using System.ComponentModel;
+using System.Globalization;
+
+namespace UnitTest.Extensions;
+
+public class ObjectExtensionsTest
+{
+ [Theory]
+ [InlineData(null, "")]
+ [InlineData("95%", "95%")]
+ [InlineData("95px", "95px")]
+ [InlineData("95", "95px")]
+ [InlineData("test", "")]
+ public static void ConvertToPercentString_Ok(string? source, string expect)
+ {
+ var actual = source.ConvertToPercentString();
+ Assert.Equal(expect, actual);
+ }
+
+ [Theory]
+ [InlineData(typeof(int?), true)]
+ [InlineData(typeof(long?), true)]
+ [InlineData(typeof(float?), true)]
+ [InlineData(typeof(short?), true)]
+ [InlineData(typeof(double?), true)]
+ [InlineData(typeof(decimal?), true)]
+ [InlineData(typeof(int), true)]
+ [InlineData(typeof(long), true)]
+ [InlineData(typeof(float), true)]
+ [InlineData(typeof(short), true)]
+ [InlineData(typeof(double), true)]
+ [InlineData(typeof(decimal), true)]
+ [InlineData(typeof(DateTime?), false)]
+ [InlineData(typeof(DateTime), false)]
+ [InlineData(typeof(string), false)]
+ public static void IsNumber_Ok(Type source, bool expect)
+ {
+ var actual = source.IsNumber();
+ Assert.Equal(expect, actual);
+ }
+
+ [Theory]
+ [InlineData(typeof(DateTime?), true)]
+ [InlineData(typeof(DateTime), true)]
+ [InlineData(typeof(DateTimeOffset?), true)]
+ [InlineData(typeof(DateTimeOffset), true)]
+ [InlineData(typeof(string), false)]
+ public static void IsDateTime_Ok(Type source, bool expect)
+ {
+ var actual = source.IsDateTime();
+ Assert.Equal(expect, actual);
+ }
+
+ [Theory]
+ [InlineData(typeof(SortOrder), "枚举")]
+ [InlineData(typeof(int), "数字")]
+ [InlineData(typeof(DateTimeOffset), "日期")]
+ [InlineData(typeof(string), "字符串")]
+ [InlineData(typeof(Foo), "字符串")]
+ public static void GetTypeDesc_Ok(Type source, string expect)
+ {
+ var actual = source.GetTypeDesc();
+ Assert.Equal(expect, actual);
+ }
+
+ [Fact]
+ public static void TryConvertTo_Ok()
+ {
+ var source = "test";
+ var result = source.TryConvertTo(typeof(string), out var v);
+ Assert.True(result);
+ Assert.Equal(source, v);
+
+ source = "123";
+ result = source.TryConvertTo(typeof(int), out var i);
+ Assert.True(result);
+ Assert.Equal(123, i);
+
+ source = "123";
+ result = source.TryConvertTo(typeof(DateTime), out var d);
+ Assert.False(result);
+ }
+
+ [Fact]
+ public static void TryConvertTo_Generic()
+ {
+ var source = "123";
+ var result = source.TryConvertTo(out var v);
+ Assert.True(result);
+ Assert.Equal(123, v);
+
+ source = null;
+ result = source.TryConvertTo(out var s);
+ Assert.True(result);
+ Assert.Null(s);
+
+ result = source.TryConvertTo(out var i);
+ Assert.True(result);
+ Assert.Equal(0, i);
+
+ source = "";
+ result = source.TryConvertTo(out var e);
+ Assert.False(result);
+
+ source = "False";
+ result = source.TryConvertTo(out var b1);
+ Assert.True(result);
+ Assert.False(b1);
+
+ source = "false";
+ result = source.TryConvertTo(out var b2);
+ Assert.True(result);
+ Assert.False(b2);
+
+ source = "test";
+ result = source.TryConvertTo(out var dt);
+ Assert.False(result);
+
+ source = typeof(Foo).Name;
+ result = source.TryConvertTo(out var f);
+ Assert.False(result);
+
+ source = typeof(Dummy).FullName;
+ result = source.TryConvertTo(out var _);
+ Assert.True(result);
+ }
+
+ [Theory]
+ [InlineData(100f, "100 B")]
+ [InlineData(1024f, "1 KB")]
+ [InlineData(1024 * 1024f, "1 MB")]
+ [InlineData(1024 * 1024 * 1024f, "1 GB")]
+ public void ToFileSizeString_Ok(long source, string expect)
+ {
+ var actual = source.ToFileSizeString();
+ Assert.Equal(expect, actual);
+ }
+
+ [Theory]
+ [InlineData(ItemChangedType.Add)]
+ [InlineData(ItemChangedType.Update)]
+ public void IsEditable_Editable(ItemChangedType itemChangedType)
+ {
+ var editorItem = new EditorItem();
+ Assert.True(editorItem.IsEditable(itemChangedType));
+ }
+
+ [Theory]
+ [InlineData(ItemChangedType.Add)]
+ [InlineData(ItemChangedType.Update)]
+ public void IsEditable_Readonly(ItemChangedType itemChangedType)
+ {
+ var editorItem = new EditorItem();
+ editorItem.SetParametersAsync(ParameterView.FromDictionary(new Dictionary
+ {
+ ["Readonly"] = true
+ }));
+ Assert.False(editorItem.IsEditable(itemChangedType));
+ }
+
+ [Theory]
+ [InlineData(ItemChangedType.Add, true)]
+ [InlineData(ItemChangedType.Add, false)]
+ public void IsEditable_IsReadonlyWhenAdd(ItemChangedType itemChangedType, bool val)
+ {
+ var editorItem = new EditorItem()
+ {
+ IsReadonlyWhenAdd = val
+ };
+ Assert.Equal(val, !editorItem.IsEditable(itemChangedType));
+ }
+
+ [Theory]
+ [InlineData(ItemChangedType.Update, true)]
+ [InlineData(ItemChangedType.Update, false)]
+ public void IsEditable_IsReadonlyWhenEdit(ItemChangedType itemChangedType, bool val)
+ {
+ var editorItem = new EditorItem()
+ {
+ IsReadonlyWhenEdit = val
+ };
+ Assert.Equal(val, !editorItem.IsEditable(itemChangedType));
+ }
+
+ [Theory]
+ [InlineData(ItemChangedType.Add)]
+ [InlineData(ItemChangedType.Update)]
+ public void IsEditable_Search(ItemChangedType itemChangedType)
+ {
+ var editorItem = new EditorItem();
+ editorItem.SetParametersAsync(ParameterView.FromDictionary(new Dictionary
+ {
+ ["Editable"] = false
+ }));
+ Assert.True(editorItem.IsEditable(itemChangedType, true));
+ }
+
+ [Fact]
+ public void CanWrite_Ok()
+ {
+ var item = new MockEditItem() { FieldName = "Name" };
+ var result = item.CanWrite(typeof(Foo));
+ Assert.True(result);
+
+ var item2 = new MockEditItem() { FieldName = "Foo.Name" };
+ result = item2.CanWrite(typeof(Dummy));
+ Assert.True(result);
+
+ item2 = new MockEditItem() { FieldName = "Count" };
+ result = item2.CanWrite(typeof(Dummy));
+ Assert.False(result);
+
+ // DynamicObject always return True
+ Assert.True(item2.CanWrite(typeof(DynamicObject)));
+ }
+
+ [Theory]
+ [InlineData("Test")]
+ [InlineData("Foo.Test")]
+ public void CanWrite_Exception(string fieldName)
+ {
+ var item = new MockEditItem() { FieldName = fieldName };
+ Assert.Throws(() => item.CanWrite(typeof(Dummy)));
+ }
+
+ [TypeConverter(typeof(DummyConverter))]
+ private class Dummy
+ {
+ public string? Name { get; set; }
+
+ public Foo Foo { get; set; } = new Foo();
+
+ public int Count { get; }
+ }
+
+ private class DummyConverter : TypeConverter
+ {
+ public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
+ {
+ return true;
+ }
+
+ public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
+ {
+ return new Dummy();
+ }
+ }
+
+ private class MockEditItem : EditorItem, IEditorItem
+ {
+ public string? FieldName { get; set; }
+
+ string IEditorItem.GetFieldName() => FieldName!;
+
+ public Dummy Dummy { get; set; } = new Dummy();
+ }
+}
diff --git a/test/UnitTest/Extensions/UtilityTest.cs b/test/UnitTest/Extensions/UtilityTest.cs
deleted file mode 100644
index 907e222674316dee43908e8d72fb69ce2ce96f8b..0000000000000000000000000000000000000000
--- a/test/UnitTest/Extensions/UtilityTest.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-// 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 BootstrapBlazor.Shared;
-
-namespace UnitTest.Extensions;
-
-public class UtilityTest : BootstrapBlazorTestBase
-{
- [Fact]
- public void GetKeyValue_Ok()
- {
- var foo = new Foo() { Id = 1 };
- var v = Utility.GetKeyValue(foo);
- Assert.Equal(1, v);
-
- object foo1 = new Foo() { Id = 2 };
- v = Utility.GetKeyValue