ASP.NET Core MVC6 中 ViewComponent 视图组件使用方法
—— ASP.NET Core 踩坑日常(二)
背景介绍
在ASP.NET5 RC1 MVC6中,微软为我们提供了一种叫做ViewComponent
视图组件的东西来替代Web Forms中的用户控件,而在MVC6之前我们只能用Partial View
来实现类似的功能。
可是。。。当我把项目升级为ASP.NET Core 1.0 RC2的时候。。。我突然发现这玩意在报错。。。
按照ASP.NET5中的使用方式,我们只需要
- 提供一个继承自
Microsoft.AspNetCore.Mvc.ViewComponent
的类,实现一个返回值为IViewComponentResult
的Invoke
或者InvokeAsync
方法; - 提供一个ViewComponent视图;
- 在其他View中调用该ViewComponent
即可。
然后在实际使用中,又经常继承一个BaseViewComponent以提供一些通用的功能,如下:
/// <summary>
/// 视图组件基类
/// <summary>
public class BaseViewComponent : ViewComponent
{
protected IConfiguration _config { get; set; }
protected ILoggerFactory _loggerFactory { get; set; }
protected ILogger logger { get; set; }
public BaseViewComponent(IConfiguration config, ILoggerFactory loggerFactory)
{
_config = config;
_loggerFactory = loggerFactory;
logger = _loggerFactory.CreateLogger(GetType().FullName);
}
}
/// <summary>
/// 视图组件类: 推荐作者
/// <summary>
[ViewComponent(Name = "RecommendedAuthors")]
public class RecommendedAuthorsViewComponent : BaseViewComponent
{
public RecommendedAuthorsViewComponent(IConfiguration config, ILoggerFactory loggerFactory) :
base(container, config, loggerFactory)
{
}
/// <summary>
/// 获取推荐作者列表
/// <summary>
public IViewComponentResult Invoke(int count)
{
var list = new List<RecommendedAuthor>(count);
return View(list);
}
}
然后,只需要在视图中调用这个ViewComponet就好了:
@Component.Invoke("RecommendedAuthors", 10/*参数*/)
在 ASP.NET Core 1.0 RC2 中的用法虽然有小小的变化,但大同小异,主要的变化目前看来就两点:
- 取消了IviewComponentHelper的同步方法Invoke,现在只保留了异步方法InvokeAsync。
- 参数的传入有些调整,不再以 params object[] 的形式传入,现在不管Invoke方法有多少个参数(即使只有一个),都需要将其组装为一个对象 new {ParamName1 = ParamValue1, ParamName2 = ParamValue2, ...}这种。
背景介绍完毕。。。
可是,当我升级到 ASP.NET Core 1.0 RC2 的时候:
"IviewComponentHelper"未包含"Invoke"的定义
"IViewComponentHellper"未包含"Invoke"的定义,并且找不到可接受第一个"IViewComponentHellper"类型参数的扩展方法"Invoke"(是否缺少using指定或程序集引用?)
怎么会找不到呢?ASP.NET5时代人家都用的好好的。没错!确实找不到了,去github上一看,现在只剩下InvokeAsync
方法了,不过好在:
如果在指定的ViewComponent上未找到异步的InvokeAsync方法时,
@Component.InvokeAsync
方法会自动调用Invoke方法。
这意味着我们在上面写好的的 RecommendedAuthorsViewComponent 类并不需要再实现一个InvokeAsync方法,有Invoke方法就足够了。
所以,我们将视图中的调用修改为:
@await Component.InvokeAsync("RecommendedAuthors", new { count = 10 })
嗯,好了,看起来貌似没什么问题了,也不报错了,那我们来运行一下吧 (○’ω’○)
在'BaseViewComponent'基类中找不到'Invoke' 或'InvokeAsync' 方法
Connection id "0HKS8H7FF0NKM": An unhandled exception was thrown by the application. System.InvalidOperationException: Could not find an 'Invoke' or 'InvokeAsync' method for the view component 'GloriousAge.Web.Common.Base.BaseViewComponent'.
看吧,我就知道没那么顺利 ㄟ( ▔, ▔ )ㄏ
可是,这玩意为啥会从我的基类上去找Invoke
方法呢?明明都由第一个参数指定了让他调用子类RecommendedAuthors的呀。。。
个人猜测:这个方法应该会查找直接继承Microsoft.AspNetCore.Mvc.ViewComponent
的第一级子类,将其实例化(因为我们声明的Invoke方法并不是static)之后,寻找并调用Invoke/InvokeAsync方法,所以才会出现这种情况。
如果上面猜想成立的话,那继续猜想:如果我们为我们的BaseViewComponent基类加上 abstract
关键字,使其不能实例化,是不是就能找到正确的类呢?
/// <summary>
/// 视图组件基类
/// <summary>
public abstract class BaseViewComponent : ViewComponent
{
// ...
}
事实证明,果然,这样就可以了!!
但是,这并不能证明上面的猜想是正确的,如果哪位知道具体原理还麻烦评论里说下,在此多谢!
至此,问题已经基本解决。下面是探寻以上解决方法时遇到的另一个小问题,记录一下,提醒自己不要再犯类似的低级错误。与君共勉。
ViewComponent 无法等待方法组 问题
探寻以上问题的解决方法时,曾试过不少方式,包括使用IviewComponentHelper
的泛型方法Component.InvokeAsync<TComponent>(object arguments)
:
@await Component.InvokeAsync<RecommendedAuthorsViewComponent>(new { count = 10 })
因为上面说了在没给基类添加abstract
修饰之前,Component.InvokeAsync(string name, object arguments)
这个非泛型方法的第一个参数虽然指定了使用子类,但是并没有什么效果,所以想要尝试使用这个泛型方法来指定ViewComponent类。
看起来貌似没什么问题,然而:
宇宙第一IDE Visual Studio给出的提示也是挺怪异的:
无法等待“方法组”。
为什么无法等待方法组?这个方法组有什么关系?
其实并不是,大家如果仔细看上面的截图就会发现,其中TComponent
是以类似字符串的红色显示的,而非类型的浅蓝色。这说明什么?明显VS并没有把他当成类型来处理,而是把他识别成了一个HTML标签,也就是说:VS认为,你的C#代码部分只有:
@await Component.InvokeAsync
所以,报出这个奇怪的无法等待方法组的提示也就不奇怪了。
那么怎么处理呢?其实也是很简单:
@(await Component.InvokeAsync<RecommendedAuthorsViewComponent>(new { count = 10 }))