InvalidOperationException rendering ViewComponent in Strongly-Typed View

半城伤御伤魂 提交于 2019-12-05 12:51:32

I've came across the same error, someone in my team updated Microsoft.AspNetCore.Mvc version from 1.0.0 to 1.1.0 and some of the components I had in Strongly-Type views started throwing

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'.

I am not sure if this change was intentional, but it is definitely not what we would expect.

I haven't got the time to research about the reasons of this 'breaking' change but I came with a solution.

Thing is, if you pass a null object to your ViewComponent.View() method, we get this exception. Any non-null object passed through it, would update the ViewData.ModelExplorer and the correct object-type would have been registered, avoiding this exception.

Using Tester-Doer pattern, same pattern used in some classes of .Net Framework, We can now pass a non-null object to the ViewComponent and use it and its wrapped object as We need.

What I did was, I created a interface IViewComponentModel<T> as class ViewComponentModel<T> as below:

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

In my ViewComponent class implementation, I 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));
}

And finally, in my Component View (.cshtml) I have the code below:

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

Now you can do whatever you want with this object inside of the view and to use your real model, just call @Model.Data and voilà.

In this way, We will never pass a null object to the ViewComponent and it won't 'inherit' the object type from the View.

Hopefully it helps!

By default it will try to pass in the model of the "Parent View" ,not sure what you would call that, try updating your code like this

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

so that partial view can have what it needs, in order to serve up it's content.

Simple solution is that, in ViewComponent class check null for view model, and if view model is null, then return empty Content result as following:

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

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

    return Content("");
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!