2

我正在尝试在 Blazor 服务器端项目中创建我的第一个 Razor 组件。Razor 组件已命名MyComponent并具有配置为从输入中检索其值的属性:

MyComponent.razor

    [Parameter]
    public int Count {get; set;}

我从通过配置的注入服务中提取计数IServiceCollection,如下所示:

    public interface ICountingService
    {
        ValueTask<int> Get();
    }

托管页面Index.razor如下所示:

@page "/"
@inject ICountingService Counter

<h1>Hello World!</h1>

<MyComponent Count="@Counter.Get()" />

但是,我似乎无法为该Count属性绑定正确的值。

我收到以下错误:

cannot convert from 'System.Threading.Tasks.ValueTask<int>' to 'int'

我发现为[Parameter]Razor 组件赋值的所有示例都是同步的,而我发现的唯一异步值是回调和方法(不是参数)。

此外,在线搜索并没有返回任何明显的结果,所以我在这里发帖希望找到答案。

请注意,我知道在其中使用protected override async Task OnInitializedAsync和存储一个值,但与上述方法相比,这似乎需要很多仪式,特别是在考虑我最终必须绑定的多个服务和属性时。

那么,如何以[Parameter]我喜欢的方式将异步调用中的值分配给 Razor 组件属性?

4

4 回答 4

1

试试这个。

@page "/"
@inject ICountingService Counter

<h1>Hello World!</h1>

<MyComponent Count="@CounterValue" />

@code{
    
     public int CounterValue {get; set;}
     protected override async Task OnInitializedAsync()
     {
           CounterValue = await Counter.Get();
     }
}
于 2020-02-20T16:17:30.710 回答
1

问题是,Counter.Get()不是 int 值;这是一个任务,现在或将来在某个未定义的点有一个 int。所以你不能将它的值分配给现在期待一个 int 的东西,因为那个 int 还不一定存在。

你已经得到了答案,虽然它“看起来像很多仪式”,但它确实是做到这一点的唯一方法:

  • 创建一个 int 属性来保存该值。
  • 声明一个异步方法
  • 在该方法中,将awaited 值分配给Counter.Get()持有该值的 int
  • 设置组件的 Count 属性等于 int 属性

这可能感觉像是很多仪式,但你应该心存感激。异步本质上是非常复杂的,拥有可用的 async/await 已经为您处理了大约 95% 的繁重工作。如果你认为这个解决方案很混乱,你应该看看在没有async/await 的情况下如何让它正确!

于 2020-02-20T16:21:36.210 回答
1

在@mason-wheeler 和@rich-bryant 提供了他们的答案之后,我进一步思考了这个问题并找到了我的解决方案,我将其发布在这里:

https://github.com/Mike-E-angelo/Blazor.ViewProperties

我称之为a ViewProperty,如下所示:

    public interface IViewProperty
    {
        ValueTask Get();
    }

    public sealed class ViewProperty<T> : IViewProperty
    {
        public static implicit operator ViewProperty<T>(ValueTask<T> instance) => new ViewProperty<T>(instance);

        readonly ValueTask<T> _source;

        public ViewProperty(ValueTask<T> source) => _source = source;

        public T Value { get; private set; }

        public bool HasValue { get; private set; }

        public async ValueTask Get()
        {
            Value    = await _source;
            HasValue = true;
        }

        public override string ToString() => Value.ToString();
    }

然后将它与组件基类型配对,然后迭代组件的视图属性并调用它们各自的异步操作:

    public abstract class ViewPropertyComponentBase : ComponentBase
    {
        protected override async Task OnParametersSetAsync()
        {
            var properties = GetType().GetRuntimeProperties();
            foreach (var metadata in properties.Where(x => x.GetCustomAttributes<ParameterAttribute>().Any() &&
                                                           typeof(IViewProperty).IsAssignableFrom(x.PropertyType)))
            {
                if (metadata.GetValue(this) is IViewProperty property)
                {
                    await property.Get().ConfigureAwait(false);
                }
            }
        }
    }

使用上述内容的示例剃须刀组件:

MyComponent.razor

@inherits ViewPropertyComponentBase

@if (Count.HasValue)
{
    <p>Your magic number is @Count.</p>
}
else
{
    <p>Loading, please wait...</p>
}


@code {

    [Parameter]
    public ViewProperty<int> Count { get; set; }

}

由此产生的使用是具有直接绑定的干净视图,不需要覆盖或其他额外的仪式:

@page "/"
@inject ICounter Count

<h1>Hello, world!</h1>

Welcome to your new app.

<MyComponent Count="@Count.Count()" />

(请注意,我发布的示例及以上使用反射,这很慢。在我使用的解决方案的实际版本中,我将成员访问编译为 lambda 表达式并缓存结果。如果你是,你可以从这里开始找到勇敢地四处寻找。)

于 2020-02-21T15:48:28.497 回答
0

感觉有点hacky,但你可以这样做:

<MyComponent Count="@Counter.Get().Result" />
于 2021-03-31T15:59:17.490 回答