Skip to content

Commit 4350928

Browse files
feat(Display): support LookupService method GetItemsByKeyAsync (#4924)
* refactor: 重构代码 Co-Authored-By: chengKun <[email protected]> * refactor: 重构 Display 代码 更新 Lookup 逻辑 Co-Authored-By: chengKun <[email protected]> * test: 增加单元测试 --------- Co-Authored-By: chengKun <[email protected]>
1 parent ece2207 commit 4350928

File tree

5 files changed

+129
-109
lines changed

5 files changed

+129
-109
lines changed

src/BootstrapBlazor/Components/Display/Display.razor

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
}
99
@if (ShowTooltip)
1010
{
11-
<Tooltip Title="@CurrentTextAsString">
11+
<Tooltip Title="@_displayText">
1212
@RenderContent
1313
</Tooltip>
1414
}
@@ -19,5 +19,5 @@ else
1919

2020
@code {
2121
RenderFragment RenderContent =>
22-
@<div @attributes="AdditionalAttributes" class="@ClassString">@CurrentTextAsString</div>;
22+
@<div @attributes="AdditionalAttributes" class="@ClassString">@_displayText</div>;
2323
}

src/BootstrapBlazor/Components/Display/Display.razor.cs

Lines changed: 49 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
55

66
using System.Collections;
7-
using System.Linq.Expressions;
87
using System.Reflection;
98

109
namespace BootstrapBlazor.Components;
@@ -18,10 +17,7 @@ public partial class Display<TValue>
1817
.AddClassFromAttributes(AdditionalAttributes)
1918
.Build();
2019

21-
/// <summary>
22-
/// 获得 显示文本
23-
/// </summary>
24-
protected string? CurrentTextAsString { get; set; }
20+
private string? _displayText;
2521

2622
/// <summary>
2723
/// 获得/设置 异步格式化字符串
@@ -56,15 +52,20 @@ public partial class Display<TValue>
5652
[Parameter]
5753
public object? LookupServiceData { get; set; }
5854

55+
/// <summary>
56+
/// <inheritdoc/>
57+
/// </summary>
58+
[Parameter]
59+
public ILookupService? LookupService { get; set; }
60+
5961
[Inject]
6062
[NotNull]
61-
private ILookupService? LookupService { get; set; }
63+
private ILookupService? InjectLookupService { get; set; }
6264

6365
/// <summary>
6466
/// 获得/设置 类型解析回调方法 组件泛型为 Array 时内部调用
6567
/// </summary>
6668
[Parameter]
67-
6869
public Func<Assembly?, string, bool, Type?>? TypeResolver { get; set; }
6970

7071
/// <summary>
@@ -73,24 +74,6 @@ public partial class Display<TValue>
7374
[Parameter]
7475
public bool ShowTooltip { get; set; }
7576

76-
/// <summary>
77-
/// <inheritdoc/>>
78-
/// </summary>
79-
/// <param name="parameters"></param>
80-
/// <returns></returns>
81-
public override Task SetParametersAsync(ParameterView parameters)
82-
{
83-
parameters.SetParameterProperties(this);
84-
85-
if (!string.IsNullOrEmpty(LookupServiceKey))
86-
{
87-
Lookup ??= LookupService.GetItemsByKey(LookupServiceKey, LookupServiceData);
88-
}
89-
90-
// For derived components, retain the usual lifecycle with OnInit/OnParametersSet/etc.
91-
return base.SetParametersAsync(ParameterView.Empty);
92-
}
93-
9477
/// <summary>
9578
/// <inheritdoc/>>
9679
/// </summary>
@@ -99,23 +82,23 @@ protected override async Task OnParametersSetAsync()
9982
{
10083
await base.OnParametersSetAsync();
10184

102-
CurrentTextAsString = await FormatTextAsString(Value);
85+
_displayText = await FormatDisplayText(Value);
10386
}
10487

10588
/// <summary>
10689
/// 数值格式化委托方法
10790
/// </summary>
10891
/// <param name="value"></param>
10992
/// <returns></returns>
110-
private async Task<string?> FormatTextAsString(TValue value) => FormatterAsync != null
93+
private async Task<string?> FormatDisplayText(TValue value) => FormatterAsync != null
11194
? await FormatterAsync(value)
11295
: (!string.IsNullOrEmpty(FormatString) && value != null
11396
? Utility.Format(value, FormatString)
11497
: value == null
11598
? FormatValueString()
116-
: FormatText(value));
99+
: await FormatText(value));
117100

118-
private string FormatText([DisallowNull] TValue value)
101+
private async Task<string> FormatText([DisallowNull] TValue value)
119102
{
120103
string ret;
121104
var type = typeof(TValue);
@@ -125,12 +108,12 @@ private string FormatText([DisallowNull] TValue value)
125108
}
126109
else if (type.IsArray)
127110
{
128-
ret = ConvertArrayToString(value);
111+
ret = ArrayConvertToString(value);
129112
}
130113
else if (type.IsGenericType && type.IsAssignableTo(typeof(IEnumerable)))
131114
{
132115
// 泛型集合 IEnumerable<TValue>
133-
ret = ConvertEnumerableToString(value);
116+
ret = await ConvertEnumerableToString(value);
134117
}
135118
else
136119
{
@@ -152,91 +135,51 @@ private string FormatValueString()
152135
return ret ?? valueString ?? string.Empty;
153136
}
154137

155-
private Func<TValue, string>? _converterArray;
156-
/// <summary>
157-
/// 获取属性方法 Lambda 表达式
158-
/// </summary>
159-
/// <returns></returns>
160-
private string ConvertArrayToString(TValue value)
138+
private Func<TValue, string>? _arrayConvertoString;
139+
private string ArrayConvertToString(TValue value)
161140
{
162-
return (_converterArray ??= ConvertArrayToStringLambda())(value);
163-
164-
Func<TValue, string> ConvertArrayToStringLambda()
165-
{
166-
Func<TValue, string> ret = _ => "";
167-
var param = Expression.Parameter(typeof(Array));
168-
var targetType = typeof(TValue).UnderlyingSystemType;
169-
var methodType = ResolveArrayType();
170-
if (methodType != null)
171-
{
172-
// 调用 string.Join<T>(",", IEnumerable<T>) 方法
173-
var method = typeof(string).GetMethods().First(m => m is { Name: "Join", IsGenericMethod: true } && m.GetParameters()[0].ParameterType == typeof(string)).MakeGenericMethod(methodType);
174-
var body = Expression.Call(method, Expression.Constant(","), Expression.Convert(param, targetType));
175-
ret = Expression.Lambda<Func<TValue, string>>(body, param).Compile();
176-
}
177-
return ret;
178-
179-
Type? ResolveArrayType()
180-
{
181-
Type? t = null;
182-
var typeName = targetType.FullName;
183-
if (!string.IsNullOrEmpty(typeName))
184-
{
185-
typeName = typeName.Replace("[]", "");
186-
if (typeName.Contains('+'))
187-
{
188-
typeName = typeName.Split('+', StringSplitOptions.RemoveEmptyEntries).Last();
189-
}
190-
t = Type.GetType(typeName, null, TypeResolver, false, true);
191-
}
192-
return t;
193-
}
194-
}
141+
_arrayConvertoString ??= LambdaExtensions.ArrayConvertToStringLambda<TValue>(TypeResolver).Compile();
142+
return _arrayConvertoString(value);
195143
}
196144

197-
private static Func<TValue, string>? _convertEnumerableToString;
198-
private static Func<TValue, IEnumerable<string>>? _convertToEnumerableString;
199-
/// <summary>
200-
/// 获取属性方法 Lambda 表达式
201-
/// </summary>
202-
/// <returns></returns>
203-
private string ConvertEnumerableToString(TValue value)
145+
private static Func<TValue, string>? _enumerableConvertToString;
146+
private async Task<string> ConvertEnumerableToString(TValue value)
204147
{
205-
return Lookup == null
206-
? (_convertEnumerableToString ??= ConvertEnumerableToStringLambda())(value)
207-
: GetTextByValue((_convertToEnumerableString ??= ConvertToEnumerableStringLambda())(value));
208-
209-
static Func<TValue, string> ConvertEnumerableToStringLambda()
210-
{
211-
var typeArguments = typeof(TValue).GenericTypeArguments;
212-
var param = Expression.Parameter(typeof(IEnumerable<>).MakeGenericType(typeArguments));
213-
var method = typeof(string).GetMethods().First(m => m is { Name: "Join", IsGenericMethod: true } && m.GetParameters()[0].ParameterType == typeof(string)).MakeGenericMethod(typeArguments);
214-
var body = Expression.Call(method, Expression.Constant(","), param);
215-
return Expression.Lambda<Func<TValue, string>>(body, param).Compile();
216-
}
148+
_enumerableConvertToString ??= LambdaExtensions.EnumerableConvertToStringLambda<TValue>().Compile();
149+
var lookup = await GetLookup();
150+
return lookup == null
151+
? _enumerableConvertToString(value)
152+
: GetTextByValue(lookup, value);
153+
}
217154

218-
static Func<TValue, IEnumerable<string>> ConvertToEnumerableStringLambda()
155+
private static Func<TValue, IEnumerable<string>>? _convertToStringEnumerable;
156+
private static string GetTextByValue(IEnumerable<SelectedItem> lookup, TValue value)
157+
{
158+
_convertToStringEnumerable ??= LambdaExtensions.ConvertToStringEnumerableLambda<TValue>().Compile();
159+
var source = _convertToStringEnumerable(value);
160+
return string.Join(",", source.Aggregate(new List<string>(), (s, i) =>
219161
{
220-
var typeArguments = typeof(TValue).GenericTypeArguments;
221-
var param = Expression.Parameter(typeof(IEnumerable<>).MakeGenericType(typeArguments));
222-
223-
var method = typeof(Display<>).MakeGenericType(typeof(TValue))
224-
.GetMethod(nameof(Cast), BindingFlags.NonPublic | BindingFlags.Static)!
225-
.MakeGenericMethod(typeArguments);
226-
var body = Expression.Call(method, param);
227-
return Expression.Lambda<Func<TValue, IEnumerable<string>>>(body, param).Compile();
228-
}
162+
var text = lookup.FirstOrDefault(d => d.Value.Equals(i, StringComparison.OrdinalIgnoreCase))?.Text;
163+
if (text != null)
164+
{
165+
s.Add(text);
166+
}
167+
return s;
168+
}));
229169
}
230170

231-
private static IEnumerable<string> Cast<TType>(IEnumerable<TType> source) => source.Select(i => i?.ToString() ?? string.Empty);
171+
private ILookupService GetLookupService() => LookupService ?? InjectLookupService;
232172

233-
private string GetTextByValue(IEnumerable<string> source) => string.Join(",", source.Aggregate(new List<string>(), (s, i) =>
173+
private IEnumerable<SelectedItem>? _lookupData;
174+
private async Task<IEnumerable<SelectedItem>?> GetLookup()
234175
{
235-
var text = Lookup!.FirstOrDefault(d => d.Value.Equals(i, StringComparison.OrdinalIgnoreCase))?.Text;
236-
if (text != null)
176+
if (Lookup != null)
237177
{
238-
s.Add(text);
178+
return Lookup;
239179
}
240-
return s;
241-
}));
180+
181+
var lookupService = GetLookupService();
182+
_lookupData ??= await lookupService.GetItemsAsync(LookupServiceKey, LookupServiceData);
183+
return _lookupData;
184+
}
242185
}

src/BootstrapBlazor/Components/Filters/LookupFilter.razor.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ protected override async Task OnParametersSetAsync()
105105
}
106106
else if (!string.IsNullOrEmpty(LookupServiceKey))
107107
{
108-
var lookupService = LookupService ?? InjectLookupService;
108+
var lookupService = GetLookupService();
109109
var lookup = await lookupService.GetItemsAsync(LookupServiceKey, LookupServiceData);
110110
if (lookup != null)
111111
{
@@ -115,6 +115,8 @@ protected override async Task OnParametersSetAsync()
115115
Items = items;
116116
}
117117

118+
private ILookupService GetLookupService() => LookupService ?? InjectLookupService;
119+
118120
/// <summary>
119121
/// <inheritdoc/>
120122
/// </summary>

src/BootstrapBlazor/Extensions/LambdaExtensions.cs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,4 +817,78 @@ public static Expression<Func<TModel, TValue>> GetKeyValue<TModel, TValue>(Type?
817817
}
818818
return ret;
819819
}
820+
821+
/// <summary>
822+
/// 数组转成字符串表达式
823+
/// </summary>
824+
/// <typeparam name="TValue"></typeparam>
825+
/// <returns></returns>
826+
/// <remarks><code><![CDATA[string.Join<T>(",", IEnumerable<T>)]]></code></remarks>
827+
public static Expression<Func<TValue, string>> EnumerableConvertToStringLambda<TValue>()
828+
{
829+
var typeArguments = typeof(TValue).GenericTypeArguments;
830+
var param = Expression.Parameter(typeof(IEnumerable<>).MakeGenericType(typeArguments));
831+
832+
var method = typeof(string).GetMethods().First(m => m is { Name: "Join", IsGenericMethod: true } && m.GetParameters()[0].ParameterType == typeof(string)).MakeGenericMethod(typeArguments);
833+
var body = Expression.Call(method, Expression.Constant(","), param);
834+
return Expression.Lambda<Func<TValue, string>>(body, param);
835+
}
836+
837+
/// <summary>
838+
/// 泛型集合转换成 <![CDATA[IEnumerable<string>]]> 方法
839+
/// </summary>
840+
/// <typeparam name="TValue"></typeparam>
841+
/// <remarks><code><![CDATA[IEnumerable<T>]]> to <![CDATA[IEnumerable<string>]]></code></remarks>
842+
/// <returns></returns>
843+
public static Expression<Func<TValue, IEnumerable<string>>> ConvertToStringEnumerableLambda<TValue>()
844+
{
845+
var typeArguments = typeof(TValue).GenericTypeArguments;
846+
var param = Expression.Parameter(typeof(IEnumerable<>).MakeGenericType(typeArguments));
847+
848+
var method = typeof(LambdaExtensions)
849+
.GetMethod(nameof(Cast), BindingFlags.NonPublic | BindingFlags.Static)!
850+
.MakeGenericMethod(typeArguments);
851+
var body = Expression.Call(method, param);
852+
return Expression.Lambda<Func<TValue, IEnumerable<string>>>(body, param);
853+
}
854+
855+
private static IEnumerable<string> Cast<TType>(IEnumerable<TType> source) => source.Select(i => i?.ToString() ?? string.Empty);
856+
857+
/// <summary>
858+
/// 数组转成字符串表达式
859+
/// </summary>
860+
/// <typeparam name="TValue"></typeparam>
861+
/// <param name="typeResolver"></param>
862+
/// <remarks><code><![CDATA[string.Join<T>(",", Array)]]></code></remarks>
863+
public static Expression<Func<TValue, string>> ArrayConvertToStringLambda<TValue>(Func<Assembly?, string, bool, Type?>? typeResolver)
864+
{
865+
Expression<Func<TValue, string>> ret = _ => "";
866+
var param = Expression.Parameter(typeof(Array));
867+
var targetType = typeof(TValue).UnderlyingSystemType;
868+
var methodType = ResolveArrayType(targetType, typeResolver);
869+
if (methodType != null)
870+
{
871+
// 调用 string.Join<T>(",", IEnumerable<T>) 方法
872+
var method = typeof(string).GetMethods().First(m => m is { Name: "Join", IsGenericMethod: true } && m.GetParameters()[0].ParameterType == typeof(string)).MakeGenericMethod(methodType);
873+
var body = Expression.Call(method, Expression.Constant(","), Expression.Convert(param, targetType));
874+
ret = Expression.Lambda<Func<TValue, string>>(body, param);
875+
}
876+
return ret;
877+
}
878+
879+
private static Type? ResolveArrayType(Type targetType, Func<Assembly?, string, bool, Type?>? typeResolver)
880+
{
881+
Type? t = null;
882+
var typeName = targetType.FullName;
883+
if (!string.IsNullOrEmpty(typeName))
884+
{
885+
typeName = typeName.Replace("[]", "");
886+
if (typeName.Contains('+'))
887+
{
888+
typeName = typeName.Split('+', StringSplitOptions.RemoveEmptyEntries).Last();
889+
}
890+
t = Type.GetType(typeName, null, typeResolver, false, true);
891+
}
892+
return t;
893+
}
820894
}

test/UnitTest/Components/DisplayTest.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public void LookupService_Ok()
4848
{
4949
var cut = Context.RenderComponent<Display<List<string>>>(pb =>
5050
{
51+
pb.Add(a => a.LookupService, null);
5152
pb.Add(a => a.LookupServiceKey, "FooLookup");
5253
pb.Add(a => a.LookupServiceData, true);
5354
pb.Add(a => a.Value, ["v1", "v2"]);

0 commit comments

Comments
 (0)