反射仅在 Blazor-State-Management 中的第一次调用时成功
Reflection only successful on first call in Blazor-State-Management
我发现了一个奇怪的行为,我完全不知道它来自哪里或如何修复它。
blazor 状态管理(基于调解器模式)出现了问题 - 可以在此处找到库:https://timewarpengineering.github.io/blazor-state/.
让我们假设我们有以下基数 class 用于枚举:
public abstract class Simple<TSimple> where TSimple: Simple<TSimple>
{
private readonly string _key;
protected Simple(string key)
{
_key = key;
}
public virtual string Key => _key;
public static TSimple Create(string key)
{
var obj = All.SingleOrDefault(e => e.Key == key);
return obj;
}
public static IReadOnlyCollection<TSimple> All => GetAll();
private static IReadOnlyCollection<TSimple> GetAll()
{
var enumerationType = typeof(TSimple);
return enumerationType.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
.Where(info => enumerationType.IsAssignableFrom(info.FieldType))
.Select(info => info.GetValue(null))
.Cast<TSimple>()
.ToArray();
}
}
以及以下枚举实现:
public class SimpleImpl : Simple<SimpleImpl>
{
public static readonly SimpleImpl One = new SimpleImpl("Important");
public static readonly SimpleImpl Two = new SimpleImpl("Urgent");
public static readonly SimpleImpl Three = new SimpleImpl("ImportantAndUrgent");
public static readonly SimpleImpl Four = new SimpleImpl("None");
private SimpleImpl(string key) : base(key)
{
}
}
到目前为止一切顺利。
我在 blazor 应用程序中使用此枚举,其中数据通过 gRPC-Web 从后端检索,转换并添加到状态。
所以 Index.cshtml 的代码部分看起来像这样:
@code
{
private AppState AppState => GetState<AppState>();
protected override async Task OnInitializedAsync()
{
foreach (var simple in new[] {"Important", "Urgent", "ImportantAndUrgent", "None"})
{
await Mediator.Send(new AppState.AddAction(simple));
}
}
这由处理程序处理:
public partial class AppState
{
public class AppHandler : ActionHandler<AddAction>
{
private AppState AppState => Store.GetState<AppState>();
public AppHandler(IStore store) : base(store)
{
}
public override async Task<Unit> Handle(AddAction aAction, CancellationToken aCancellationToken)
{
var simple = SimpleImpl.Create(aAction.Simple);
Console.WriteLine(simple == null); // First call false, afterwards true
AppState.Simples.Add(simple); // If I don't add the object to the state, Simple.Create always returns an object
return await Unit.Task;
}
}
}
这就是问题所在。第一次尝试一切正常,但如果第二次调用函数(所以我的 gRPC-Client returns 多个项目)simple
将始终为空。如果我删除 AppState.Simples.Add(simple)
然后它再次工作。
如果我添加以下代码:Console.WriteLine(string.Join(",", SimpleImpl.All.Select(s => s.Key));
在第一个 运行 上,它会打印所有可能的值:
Important,Urgent,ImportantAndUrgent,None
第二个运行,这个:
,Urgent,,
Urgent
在第一个 运行 中的 Dto 中。所以这似乎与列表中的引用如何保持活动有关(这不应该干扰 Simple
中的反射部分的工作方式)。
此外:在 Simple
的 GetAll()
函数中一切正常,直到 Select(info => .GetValue(null))
FieldInfo-属性 本身包含所有 4 个选项。在 GetValue
和演员表之后,只有最后选择的一个“活着”。
状态实体如下所示:
public partial class AppState : State<AppState>
{
public IList<SimpleImpl> Simples { get; private set; }
public override void Initialize()
{
Simples = new List<SimpleImpl>();
}
}
以及此示例的操作:
public partial class AppState
{
public class AddAction : IAction
{
public AddAction(string simple)
{
Simple = simple;
}
public string Simple { get; }
}
}
此代码 运行ning 在 .NET Core 3.1 下。
如果有人知道问题出在哪里,我将不胜感激。
感谢 @steven-t-cramer 帮助我找到问题。
基本上这一切都归结为 Mediator.Send
和 State-Handling.
在 Blazor-State 库中,当一个人调度和处理一个动作时会创建一个克隆(因此您作为开发人员不必处理这个问题)。但正是由于 Simple
(基本上是枚举 class)的静态性质,这种克隆在这里搞砸了很多时间。
为了解决这个问题,国家可以实施 ICloneable
并自行完成这些工作。
一种非常天真的做法是:
public partial class AppState : State<AppState>, ICloneable
{
private List<SimpleImpl> _simples = new List<SimpleImpl>();
public IReadOnlyList<SimpleImpl> Simples => _simples.AsReadOnly();
public override void Initialize()
{
_simples = new List<SimpleImpl>();
}
public object Clone()
{
var state = new AppState { _simples = _simples};
return state;
}
}
我发现了一个奇怪的行为,我完全不知道它来自哪里或如何修复它。 blazor 状态管理(基于调解器模式)出现了问题 - 可以在此处找到库:https://timewarpengineering.github.io/blazor-state/.
让我们假设我们有以下基数 class 用于枚举:
public abstract class Simple<TSimple> where TSimple: Simple<TSimple>
{
private readonly string _key;
protected Simple(string key)
{
_key = key;
}
public virtual string Key => _key;
public static TSimple Create(string key)
{
var obj = All.SingleOrDefault(e => e.Key == key);
return obj;
}
public static IReadOnlyCollection<TSimple> All => GetAll();
private static IReadOnlyCollection<TSimple> GetAll()
{
var enumerationType = typeof(TSimple);
return enumerationType.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)
.Where(info => enumerationType.IsAssignableFrom(info.FieldType))
.Select(info => info.GetValue(null))
.Cast<TSimple>()
.ToArray();
}
}
以及以下枚举实现:
public class SimpleImpl : Simple<SimpleImpl>
{
public static readonly SimpleImpl One = new SimpleImpl("Important");
public static readonly SimpleImpl Two = new SimpleImpl("Urgent");
public static readonly SimpleImpl Three = new SimpleImpl("ImportantAndUrgent");
public static readonly SimpleImpl Four = new SimpleImpl("None");
private SimpleImpl(string key) : base(key)
{
}
}
到目前为止一切顺利。 我在 blazor 应用程序中使用此枚举,其中数据通过 gRPC-Web 从后端检索,转换并添加到状态。
所以 Index.cshtml 的代码部分看起来像这样:
@code
{
private AppState AppState => GetState<AppState>();
protected override async Task OnInitializedAsync()
{
foreach (var simple in new[] {"Important", "Urgent", "ImportantAndUrgent", "None"})
{
await Mediator.Send(new AppState.AddAction(simple));
}
}
这由处理程序处理:
public partial class AppState
{
public class AppHandler : ActionHandler<AddAction>
{
private AppState AppState => Store.GetState<AppState>();
public AppHandler(IStore store) : base(store)
{
}
public override async Task<Unit> Handle(AddAction aAction, CancellationToken aCancellationToken)
{
var simple = SimpleImpl.Create(aAction.Simple);
Console.WriteLine(simple == null); // First call false, afterwards true
AppState.Simples.Add(simple); // If I don't add the object to the state, Simple.Create always returns an object
return await Unit.Task;
}
}
}
这就是问题所在。第一次尝试一切正常,但如果第二次调用函数(所以我的 gRPC-Client returns 多个项目)simple
将始终为空。如果我删除 AppState.Simples.Add(simple)
然后它再次工作。
如果我添加以下代码:Console.WriteLine(string.Join(",", SimpleImpl.All.Select(s => s.Key));
在第一个 运行 上,它会打印所有可能的值:
Important,Urgent,ImportantAndUrgent,None
第二个运行,这个:
,Urgent,,
Urgent
在第一个 运行 中的 Dto 中。所以这似乎与列表中的引用如何保持活动有关(这不应该干扰 Simple
中的反射部分的工作方式)。
此外:在 Simple
的 GetAll()
函数中一切正常,直到 Select(info => .GetValue(null))
FieldInfo-属性 本身包含所有 4 个选项。在 GetValue
和演员表之后,只有最后选择的一个“活着”。
状态实体如下所示:
public partial class AppState : State<AppState>
{
public IList<SimpleImpl> Simples { get; private set; }
public override void Initialize()
{
Simples = new List<SimpleImpl>();
}
}
以及此示例的操作:
public partial class AppState
{
public class AddAction : IAction
{
public AddAction(string simple)
{
Simple = simple;
}
public string Simple { get; }
}
}
此代码 运行ning 在 .NET Core 3.1 下。 如果有人知道问题出在哪里,我将不胜感激。
感谢 @steven-t-cramer 帮助我找到问题。
基本上这一切都归结为 Mediator.Send
和 State-Handling.
在 Blazor-State 库中,当一个人调度和处理一个动作时会创建一个克隆(因此您作为开发人员不必处理这个问题)。但正是由于 Simple
(基本上是枚举 class)的静态性质,这种克隆在这里搞砸了很多时间。
为了解决这个问题,国家可以实施 ICloneable
并自行完成这些工作。
一种非常天真的做法是:
public partial class AppState : State<AppState>, ICloneable
{
private List<SimpleImpl> _simples = new List<SimpleImpl>();
public IReadOnlyList<SimpleImpl> Simples => _simples.AsReadOnly();
public override void Initialize()
{
_simples = new List<SimpleImpl>();
}
public object Clone()
{
var state = new AppState { _simples = _simples};
return state;
}
}