diff --git a/src/BootstrapBlazor.Shared/Data/CustomDynamicObjectData.cs b/src/BootstrapBlazor.Shared/Data/CustomDynamicColumnsObjectData.cs similarity index 50% rename from src/BootstrapBlazor.Shared/Data/CustomDynamicObjectData.cs rename to src/BootstrapBlazor.Shared/Data/CustomDynamicColumnsObjectData.cs index 9aa6472a611590dcad96fe1829495077bc9d3d9d..420d4a44c5de7a1c0074034b67a10fc7bd64fc91 100644 --- a/src/BootstrapBlazor.Shared/Data/CustomDynamicObjectData.cs +++ b/src/BootstrapBlazor.Shared/Data/CustomDynamicColumnsObjectData.cs @@ -7,49 +7,37 @@ namespace BootstrapBlazor.Shared; /// /// /// -public class BootstrapBlazorDynamicObjectData : IDynamicObject +public class CustomDynamicColumnsObjectData : DynamicColumnsObject { /// /// /// - public string Fix { get; set; } = ""; + public string? Fix { get; set; } /// /// /// - public Dictionary Dynamic { get; set; } - - /// - /// - /// - public Guid DynamicObjectPrimaryKey { get => new(Fix); set { } } + public CustomDynamicColumnsObjectData() : this("", new()) { } /// /// /// /// /// - public BootstrapBlazorDynamicObjectData(string fix, Dictionary data) + public CustomDynamicColumnsObjectData(string? fix, Dictionary data) { Fix = fix; - Dynamic = data; + Columns = data; } - /// - /// - /// - public BootstrapBlazorDynamicObjectData() : this("", new()) { } - /// /// /// /// /// - public object? GetValue(string propertyName) + public override object? GetValue(string propertyName) { - if (propertyName == nameof(Fix)) - return Fix; - return Dynamic.TryGetValue(propertyName, out string? v) ? v : ""; + return propertyName == nameof(Fix) ? Fix : base.GetValue(propertyName); } /// @@ -57,12 +45,10 @@ public class BootstrapBlazorDynamicObjectData : IDynamicObject /// /// /// - public void SetValue(string propertyName, object? value) + public override void SetValue(string propertyName, object? value) { - if (value is not string str) - return; if (propertyName == nameof(Fix)) - Fix = str; - Dynamic[propertyName] = str; + Fix = value?.ToString(); + Columns[propertyName] = value; } } diff --git a/src/BootstrapBlazor.Shared/Data/CustomDynamicData.cs b/src/BootstrapBlazor.Shared/Data/CustomDynamicData.cs index 63f72d56913da4c306d66dcf43cbef6ff6e7b8d9..91b7d3c7b98da7a47a318fbd388c664d107c3177 100644 --- a/src/BootstrapBlazor.Shared/Data/CustomDynamicData.cs +++ b/src/BootstrapBlazor.Shared/Data/CustomDynamicData.cs @@ -12,14 +12,14 @@ namespace BootstrapBlazor.Shared; public class CustomDynamicData : System.Dynamic.DynamicObject { /// - /// + /// 获得/设置 固定列 /// public string Fix { get; set; } = ""; /// /// 存储每列值信息 Key 列名 Value 为列值 /// - public Dictionary Dynamic { get; set; } + public Dictionary Columns { get; set; } /// /// @@ -29,7 +29,7 @@ public class CustomDynamicData : System.Dynamic.DynamicObject public CustomDynamicData(string fix, Dictionary data) { Fix = fix; - Dynamic = data; + Columns = data; } /// @@ -37,12 +37,6 @@ public class CustomDynamicData : System.Dynamic.DynamicObject /// public CustomDynamicData() : this("", new()) { } - /// - public override IEnumerable GetDynamicMemberNames() - { - return Dynamic.Keys.Append(nameof(Fix)); - } - /// /// /// @@ -55,9 +49,9 @@ public class CustomDynamicData : System.Dynamic.DynamicObject { result = Fix; } - else if (Dynamic.ContainsKey(binder.Name)) + else if (Columns.ContainsKey(binder.Name)) { - result = Dynamic[binder.Name]; + result = Columns[binder.Name]; } else { @@ -82,9 +76,9 @@ public class CustomDynamicData : System.Dynamic.DynamicObject Fix = v; ret = true; } - else if (Dynamic.ContainsKey(binder.Name)) + else if (Columns.ContainsKey(binder.Name)) { - Dynamic[binder.Name] = v; + Columns[binder.Name] = v; ret = true; } return ret; diff --git a/src/BootstrapBlazor.Shared/Samples/Table/TablesDynamicObject.razor b/src/BootstrapBlazor.Shared/Samples/Table/TablesDynamicObject.razor index 17f30af9bcb6cec298013309d57cfaf741c4c30f..3b5adcc09bca10dfb7fe09027cb35e8f8da66340 100644 --- a/src/BootstrapBlazor.Shared/Samples/Table/TablesDynamicObject.razor +++ b/src/BootstrapBlazor.Shared/Samples/Table/TablesDynamicObject.razor @@ -11,20 +11,28 @@ @foreach (var element in DynamicColumnList) { - + + + } - @foreach (var element in StaticColumnList) { - + + + }
diff --git a/src/BootstrapBlazor.Shared/Samples/Table/TablesDynamicObject.razor.cs b/src/BootstrapBlazor.Shared/Samples/Table/TablesDynamicObject.razor.cs index 1a90032dd7646d859de0216c0a97c2e6994273a0..430cb736fbf0a995d22b4dffc0b88541a29b9fec 100644 --- a/src/BootstrapBlazor.Shared/Samples/Table/TablesDynamicObject.razor.cs +++ b/src/BootstrapBlazor.Shared/Samples/Table/TablesDynamicObject.razor.cs @@ -10,7 +10,7 @@ namespace BootstrapBlazor.Shared.Samples.Table; public partial class TablesDynamicObject { [NotNull] - private IEnumerable? BootstrapDynamicItems { get; set; } + private IEnumerable? CustomDynamicItems { get; set; } private static List StaticColumnList => new() { "A", "B", "C", "Z" }; @@ -28,8 +28,8 @@ public partial class TablesDynamicObject var now = DateTime.Now; DynamicColumnList = Enumerable.Range(1, 5).Select(index => now.AddMinutes(-1 * index).ToString("HH:mm")).ToList(); - BootstrapDynamicItems = Enumerable.Range(1, 10) - .Select(index => new BootstrapBlazorDynamicObjectData(index.ToString(), StaticColumnList.ToDictionary(d => d, d => $"{d}{index}"))); + CustomDynamicItems = Enumerable.Range(1, 10) + .Select(index => new CustomDynamicColumnsObjectData(index.ToString(), StaticColumnList.ToDictionary(d => d, d => (object?)$"{d}{index}"))); } private readonly static Random random = new(); diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj index e449b8dd45e7065936286082d480ed8a5ae80b0a..b6ba1da869b6eb54fce1ddd342fd579128191997 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -1,7 +1,7 @@ - 6.11.15 + 6.11.16 diff --git a/src/BootstrapBlazor/Components/Clipboard/Clipboard.cs b/src/BootstrapBlazor/Components/Clipboard/Clipboard.cs index 729200d6b441b3e5f928a4f6ed2ef2402f6a658b..a418543c4c490a79bb0fb20277260ded800ce24c 100644 --- a/src/BootstrapBlazor/Components/Clipboard/Clipboard.cs +++ b/src/BootstrapBlazor/Components/Clipboard/Clipboard.cs @@ -50,11 +50,12 @@ public class Clipboard : BootstrapModuleComponentBase /// Dispose 方法 ///
/// - protected virtual void Dispose(bool disposing) + protected override async ValueTask DisposeAsync(bool disposing) { if (disposing) { ClipboardService.UnRegister(this); } + await base.DisposeAsync(disposing); } } diff --git a/src/BootstrapBlazor/Dynamic/DynamicColumnsObject.cs b/src/BootstrapBlazor/Dynamic/DynamicColumnsObject.cs new file mode 100644 index 0000000000000000000000000000000000000000..1381bd088750555c7629367d9500a9572272d535 --- /dev/null +++ b/src/BootstrapBlazor/Dynamic/DynamicColumnsObject.cs @@ -0,0 +1,55 @@ +// 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/ + +namespace BootstrapBlazor.Components; + +/// +/// 动态列类 +/// +public class DynamicColumnsObject : IDynamicColumnsObject +{ + /// + /// + /// + public Dictionary Columns { get; set; } + + /// + /// 获得/设置 行主键 + /// + public Guid DynamicObjectPrimaryKey { get; set; } + + /// + /// 构造函数 + /// + /// + public DynamicColumnsObject(Dictionary columnsData) + { + Columns = columnsData; + } + + /// + /// 构造函数 + /// + public DynamicColumnsObject() : this(new()) { } + + /// + /// + /// + /// + /// + public virtual object? GetValue(string propertyName) + { + return Columns.TryGetValue(propertyName, out object? v) ? v : null; + } + + /// + /// + /// + /// + /// + public virtual void SetValue(string propertyName, object? value) + { + Columns[propertyName] = value; + } +} diff --git a/src/BootstrapBlazor/Dynamic/IDynamicColumnsObject.cs b/src/BootstrapBlazor/Dynamic/IDynamicColumnsObject.cs new file mode 100644 index 0000000000000000000000000000000000000000..2b1872d92abdc1dd7c82020f1ab724c889dfb4ae --- /dev/null +++ b/src/BootstrapBlazor/Dynamic/IDynamicColumnsObject.cs @@ -0,0 +1,16 @@ +// 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/ + +namespace BootstrapBlazor.Components; + +/// +/// 动态对象接口 +/// +public interface IDynamicColumnsObject : IDynamicObject +{ + /// + /// 获得设置 列与列数值集合 + /// + public Dictionary Columns { get; set; } +} diff --git a/src/BootstrapBlazor/Services/CacheManager.cs b/src/BootstrapBlazor/Services/CacheManager.cs index a851df8ab1ac09e4239af1f8cdccdcc1db94f828..821ca73398b74451a2ddda6c99710025a6c51b47 100644 --- a/src/BootstrapBlazor/Services/CacheManager.cs +++ b/src/BootstrapBlazor/Services/CacheManager.cs @@ -467,14 +467,22 @@ internal class CacheManager : ICacheManager { throw new ArgumentNullException(nameof(model)); } - var type = model.GetType(); - var cacheKey = ($"Lambda-Get-{type.FullName}", typeof(TModel), fieldName, typeof(TResult)); - var invoker = Instance.GetOrCreate(cacheKey, entry => + + return (model is IDynamicColumnsObject d) + ? (TResult)d.GetValue(fieldName)! + : GetValue(); + + TResult GetValue() { - entry.SetDynamicAssemblyPolicy(type); - return LambdaExtensions.GetPropertyValueLambda(model, fieldName).Compile(); - }); - return invoker(model); + var type = model.GetType(); + var cacheKey = ($"Lambda-Get-{type.FullName}", typeof(TModel), fieldName, typeof(TResult)); + var invoker = Instance.GetOrCreate(cacheKey, entry => + { + entry.SetDynamicAssemblyPolicy(type); + return LambdaExtensions.GetPropertyValueLambda(model, fieldName).Compile(); + }); + return invoker(model); + } } public static void SetPropertyValue(TModel model, string fieldName, TValue value) @@ -483,14 +491,27 @@ internal class CacheManager : ICacheManager { throw new ArgumentNullException(nameof(model)); } - var type = model.GetType(); - var cacheKey = ($"Lambda-Set-{type.FullName}", typeof(TModel), fieldName, typeof(TValue)); - var invoker = Instance.GetOrCreate(cacheKey, entry => + + if (model is IDynamicColumnsObject d) { - entry.SetDynamicAssemblyPolicy(type); - return LambdaExtensions.SetPropertyValueLambda(model, fieldName).Compile(); - }); - invoker(model, value); + d.SetValue(fieldName, value); + } + else + { + SetValue(); + } + + void SetValue() + { + var type = model.GetType(); + var cacheKey = ($"Lambda-Set-{type.FullName}", typeof(TModel), fieldName, typeof(TValue)); + var invoker = Instance.GetOrCreate(cacheKey, entry => + { + entry.SetDynamicAssemblyPolicy(type); + return LambdaExtensions.SetPropertyValueLambda(model, fieldName).Compile(); + }); + invoker(model, value); + } } /// diff --git a/test/UnitTest/Dynamic/DynamicColumnsObjectTest.cs b/test/UnitTest/Dynamic/DynamicColumnsObjectTest.cs new file mode 100644 index 0000000000000000000000000000000000000000..2ae7815a90610e8cd409c1da6779f821bc3b0552 --- /dev/null +++ b/test/UnitTest/Dynamic/DynamicColumnsObjectTest.cs @@ -0,0 +1,55 @@ +// 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/ + +namespace UnitTest.Dynamic; + +public class DynamicColumnsObjectTest +{ + [Fact] + public void DynamicColumnsObject_Ok() + { + var data = new DynamicColumnsObject(); + Assert.Empty(data.Columns); + } + + [Fact] + public void DynamicColumnsObject_Columns() + { + var data = new DynamicColumnsObject(new Dictionary() + { + ["A"] = "1", + ["B"] = 1 + }); + + Assert.Equal("1", data.Columns["A"]); + Assert.Equal(1, data.Columns["B"]); + } + + [Fact] + public void DynamicColumnsObject_Key() + { + var guid = Guid.NewGuid(); + var data = new DynamicColumnsObject() + { + DynamicObjectPrimaryKey = guid + }; + + Assert.Equal(guid, data.DynamicObjectPrimaryKey); + } + + [Fact] + public void DynamicColumnsObject_Value() + { + var data = new DynamicColumnsObject(); + data.Columns["A"] = "1"; + Assert.Equal("1", data.Columns["A"]); + + data.SetValue("A", 2); + Assert.Equal(2, data.GetValue("A")); + Assert.Equal(2, data.Columns["A"]); + + Assert.Null(data.GetValue("B")); + Assert.Throws(() => data.Columns["B"]); + } +} diff --git a/test/UnitTest/Services/ClipboardTest.cs b/test/UnitTest/Services/ClipboardTest.cs new file mode 100644 index 0000000000000000000000000000000000000000..1fb00092a127d0ed572102301bca87a07a7756b9 --- /dev/null +++ b/test/UnitTest/Services/ClipboardTest.cs @@ -0,0 +1,33 @@ +// 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 Microsoft.Extensions.DependencyInjection; + +namespace UnitTest.Services; + +public class ClipboardServiceTest : BootstrapBlazorTestBase +{ + [Fact] + public void ClipboardService_Null() + { + var service = Context.Services.GetRequiredService(); + Assert.ThrowsAsync(() => service.Copy("test")); + } + + [Fact] + public void ClipboardService_Ok() + { + var service = Context.Services.GetRequiredService(); + var cut = Context.RenderComponent(); + var copied = false; + service.Copy("Test", () => + { + copied = true; + return Task.CompletedTask; + }); + Assert.True(copied); + + _ = cut.Instance.DisposeAsync(); + } +}