InvalidOperationException 在强类型视图中渲染 ViewComponent

InvalidOperationException rendering ViewComponent in Strongly-Typed View

最近将 dotnet core 1.0.1 更新为 1.1,MVC 中的 ViewComponent 开始失败,出现以下异常:

InvalidOperationException: One or more errors occurred. (The model item passed into the ViewDataDictionary is of type 'App.Models.HomeViewModel', but this ViewDataDictionary instance requires a model item of type 'App.Components.LoginViewComponent'.)

Index.cshtml 呈现 LoginViewComponent:

@model App.Models.HomeViewModel
<html>
   @Component.InvokeAsync("LoginViewComponent")
</html>

Views/Home/Index.cshtml

ViewComponent LoginViewComponent/Default.cshtml 只是一个显示模型信息的 class:

@model App.Components.LoginViewCompoent
<body>
    <div>@Html.DisplayFor(v=>v.LoginName)</div>
</body>

视图/共享/组件/LoginViewComponent/Default.cshtml

当从 Default.cshtml.

中删除 @model 指令时,它呈现得很好

难道 ViewComponent 不应该与包装它的父视图分离且不可知吗?从异常来看,似乎需要在 HomeViewModel class 中声明 LoginViewComponent ViewComponent 才能呈现它。

在 asp.net 核心 github 上找不到关于此的任何更改说明。

对此发表评论和提供帮助将不胜感激。

默认情况下它会尝试传入 "Parent View" 的模型,不知道你会怎么称呼它,尝试像这样更新你的代码

 @Component.InvokeAsync("LoginViewComponent", new App.Components.LoginViewComponent())

以便部分视图可以拥有它需要的内容,以便提供它的内容。

我遇到了同样的错误,我团队中有人将 Microsoft.AspNetCore.Mvc 版本从 1.0.0 更新到 1.1.0,我在强类型视图中的一些组件开始抛出

InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'My.StronglyView.ObjectType', but this ViewDataDictionary instance requires a model item of type 'My.ViewComponent.ObjectType'.

我不确定此更改是否是有意的,但这绝对不是我们所期望的。

我没有时间研究这个 'breaking' 变化的原因,但我想出了一个解决方案。

事实是,如果您将 null 对象传递给 ViewComponent.View() 方法,我们会得到此异常。任何通过它的非空对象都会更新 ViewData.ModelExplorer 并且正确的 object-type 会被注册,避免这个异常。

使用 Tester-Doer 模式,与 .Net Framework 的某些 classes 中使用的相同模式,我们现在可以将非空对象传递给 ViewComponent 并使用它及其包装的对象正如我们所需要的。

我所做的是,我创建了一个接口 IViewComponentModel<T> 作为 class ViewComponentModel<T> 如下:

// Interface for ViewComponentModel
public interface IViewComponentModel<T> 
    where T : class
{
    T Data { get; }
    bool HasData();
}

// ViewComponentModel class for the Strongly-Type View Component
public class ViewComponentModel<T> : IViewComponentModel<T>
    where T : class
{
    public T Data { get; private set; }

    public ViewComponentModel(T data)
    {
        Data = data;
    }

    public bool HasData() => Data != null;
}

在我的 ViewComponent class 实现中,我 return View(new ViewComponentModel<AnyReferenceType>(myModel));

public async Task<IViewComponentResult> InvokeAsync()
{
    var myViewData = _myService.GetSomeObjectForMyViewComponent();
    var model = new ViewComponentModel<MyViewDataType>(myViewData);

    return await Task.FromResult(View(model));
}

最后,在我的组件视图 (.cshtml) 中,我有以下代码:

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@model ViewComponentModel<MyViewDataType>

现在您可以在视图中使用此对象做任何您想做的事情,并使用您的真实模型,只需调用 @Model.Data 即可。

这样,我们将永远不会将空对象传递给 ViewComponent,也不会 'inherit' 来自 View 的对象类型。

希望对您有所帮助!

简单的解决方案是,在ViewComponent中class检查视图模型是否为空,如果视图模型为空,则return空内容结果如下:

public async Task<IViewComponentResult> InvokeAsync()
{
    var vm = some query for ViewModel;

    if(vm != null)
    {
        return View(vm);
    }

    return Content("");
}