【问题标题】:Reflection only successful on first call in Blazor-State-Management反射仅在 Blazor-State-Management 中的第一次调用时成功
【发布时间】:2021-01-18 21:44:46
【问题描述】:

我发现了一种奇怪的行为,我完全不知道它来自哪里或如何解决它。 问题出现在 blazor 状态管理(基于中介模式)- 可以在此处找到库:https://timewarpengineering.github.io/blazor-state/

假设我们有以下枚举基类:

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));
    }
}

这由 Handler 处理:

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 客户端返回多个项目)simple 将始终为空。如果我删除 AppState.Simples.Add(simple) 然后它会再次工作。 如果我在第一次运行时添加以下代码:Console.WriteLine(string.Join(",", SimpleImpl.All.Select(s =&gt; s.Key));,它会打印所有可能的值:

重要、紧急、重要且紧急、无

在第二次运行时,这个:

,紧急,,,

Urgent 在第一次运行时位于 Dto 中。所以这似乎与 List 中的引用如何保持活动有关(这不应干扰Simple 中的反射部分的工作方式)。

此外:在 SimpleGetAll() 函数中,一切正常,直到 Select(info =&gt; .GetValue(null)) FieldInfo-Property 本身包含所有 4 个选项。在GetValue 和演员之后,只有最后一个被选中的“活着”。

State-Entity 如下所示:

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; }
    }
}

此代码在 .NET Core 3.1 下运行。 如果有人有问题所在的提示,我将非常感激。

【问题讨论】:

  • 能否添加 blazor-webassembly 或 blazor-server-side 标记?
  • 添加了 webassembly(客户端 blazor)
  • 它应该可以工作。尝试制作一个minimal reproducible example,没有目标和 grpc 等。我将您的 Simple 代码粘贴到一个新的废品项目中,但无法重现。
  • ping @steventcramer
  • 我将调整我的示例并删除 gRPC-Part,因为它不相关

标签: c# .net-core state blazor blazor-webassembly


【解决方案1】:

感谢@steven-t-cramer 如何帮助我找到问题。 基本上这一切都归结为Mediator.Send 和状态处理。

在 Blazor-State 库中,在调度和处理操作时会创建一个克隆(因此,作为开发人员,您不必关心这些)。但正是由于Simple(基本上是一个枚举类)的静态性质,这种克隆在这里搞砸了。

为了解决这个问题,国家可以实施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;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-01-21
    • 1970-01-01
    • 2017-07-25
    • 2020-06-16
    • 2018-07-22
    • 2012-07-31
    • 2021-11-06
    • 1970-01-01
    相关资源
    最近更新 更多