Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/BootstrapBlazor/BootstrapBlazor.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<Version>9.4.5</Version>
<Version>9.4.6</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
14 changes: 12 additions & 2 deletions src/BootstrapBlazor/Components/Table/Table.razor.Edit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -294,18 +294,28 @@ private async Task InternalOnAddAsync()
[Parameter]
public Func<TItem>? CreateItemCallback { get; set; }

/// <summary>
/// Get or sets Whether to automatically initialize model properties default value is false.
/// </summary>
[Parameter]
public bool IsAutoInitializeModelProperty { get; set; }

private TItem CreateTItem() => CreateItemCallback?.Invoke() ?? CreateInstance();

private readonly string ErrorMessage = $"{typeof(TItem)} create instrance failed. Please provide {nameof(CreateItemCallback)} create the {typeof(TItem)} instance. {typeof(TItem)} 自动创建实例失败,请通过 {nameof(CreateItemCallback)} 回调方法手动创建实例";

private TItem CreateInstance()
{
TItem? item;
try
{
return ObjectExtensions.CreateInstanceWithCascade<TItem>();
item = ObjectExtensions.CreateInstance<TItem>(IsAutoInitializeModelProperty);
}
catch (Exception ex)
{
throw new InvalidOperationException($"{typeof(TItem)} missing new() method. Please provide {nameof(CreateItemCallback)} create the {typeof(TItem)} instance. {typeof(TItem)} 未提供无参构造函数 new() 请通过 {nameof(CreateItemCallback)} 回调方法创建实例", ex);
throw new InvalidOperationException(ErrorMessage, ex);
}
return item!;
}

/// <summary>
Expand Down
58 changes: 45 additions & 13 deletions src/BootstrapBlazor/Extensions/ObjectExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,37 +242,69 @@ internal static void Clone<TModel>(this TModel source, TModel item)
/// Creates an instance of a type and ensures all class-type properties are initialized.
/// </summary>
/// <typeparam name="TItem">The type to create an instance of.</typeparam>
Comment thread
ArgoZhang marked this conversation as resolved.
/// <param name="isAutoInitializeModelProperty">Whether to automatically initialize model properties default value is false.</param>
/// <returns>An instance of the specified type with initialized properties.</returns>
public static TItem CreateInstanceWithCascade<TItem>()
public static TItem? CreateInstance<TItem>(bool isAutoInitializeModelProperty = false)
{
var instance = Activator.CreateInstance<TItem>();
instance!.EnsureInitialized();
TItem? instance;
try
{
instance = Activator.CreateInstance<TItem>();
if (isAutoInitializeModelProperty)
{
instance.EnsureInitialized(isAutoInitializeModelProperty);
}
}
catch
{
throw;
}
return instance;
}

private static object? CreateInstance(Type type, bool isAutoInitializeModelProperty = false)
{
object? instance;
try
{
instance = Activator.CreateInstance(type);
if (isAutoInitializeModelProperty)
{
instance.EnsureInitialized();
}
}
catch
{
throw;
}
return instance;
}

/// <summary>
/// Ensures that all class-type properties of the instance are initialized.
/// </summary>
/// <param name="isAutoInitializeModelProperty">Whether to automatically initialize model properties default value is false.</param>
/// <param name="instance">The instance to initialize properties for.</param>
private static void EnsureInitialized(this object instance)
private static void EnsureInitialized(this object? instance, bool isAutoInitializeModelProperty = false)
{
if (instance is null)
{
return;
}

// Reflection performance needs to be optimized here
foreach (var propertyInfo in instance.GetType().GetProperties().Where(p => p.PropertyType.IsClass && p.PropertyType != typeof(string)))
{
var type = propertyInfo.PropertyType;
var value = propertyInfo.GetValue(instance, null);
if (value is null)
{
var pv = CreateInstance(type);
propertyInfo.SetValue(instance, pv);
var pv = CreateInstance(type, isAutoInitializeModelProperty);
if (pv is not null)
{
propertyInfo.SetValue(instance, pv);
}
}
}
}

private static object? CreateInstance(Type type)
{
var instance = Activator.CreateInstance(type);
instance!.EnsureInitialized();
return instance;
}
}
1 change: 1 addition & 0 deletions test/UnitTest/Components/TableTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ public async Task Items_Add(InsertRowMode insertMode, bool bind)
{
pb.AddChildContent<Table<Foo>>(pb =>
{
pb.Add(a => a.IsAutoInitializeModelProperty, true);
Comment thread
ArgoZhang marked this conversation as resolved.
pb.Add(a => a.Items, items);
if (bind)
{
Expand Down
21 changes: 21 additions & 0 deletions test/UnitTest/Extensions/ObjectExtensionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,27 @@ public void IsStatic_Ok()
Assert.True(pi.IsStatic());
}

[Fact]
public void CreateInstance_Ok()
{
var exception = Assert.ThrowsAny<Exception>(() => ObjectExtensions.CreateInstance<MockComplexObject>(true));
Comment thread
ArgoZhang marked this conversation as resolved.

var mi = typeof(ObjectExtensions).GetMethod("EnsureInitialized", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
Assert.NotNull(mi);
mi.Invoke(null, [null, false]);

var instance = ObjectExtensions.CreateInstance<MockComplexObject>(false);
Assert.NotNull(instance);
Assert.Null(instance.Test);
}

private class MockComplexObject
{
public Foo? Foo { get; set; }

public (string Name, int Count)[]? Test { get; set; }
}

private class MockStatic
{
private static int _test;
Expand Down