asp.net 核心视图组件已执行但未呈现标记

asp.net Core View Component executed but markup not rendered

我有一个使用 VS.2017 开发的 ASP.NET Core 1.1 Web 应用程序,我决定将一些视图功能放在视图组件中(之前已经做过其他的)。

此视图组件获取与用户 ID 关联的权限字典集合,并以漂亮的方式显示它们 table。当我将它作为页面的一部分(而不是 VC)时,它会起作用。但是当我将它用作视图组件时,组件的 HTML 永远不会呈现。

我在视图组件中放置了一个断点,它触发了,我看到 View(granted) return 语句 return 一个填充列表,直到那里按预期执行。

然后我在@{ some code here } 顶部的 ViewComponent 的 default.cshtml 代码部分放置了一个断点,并且该断点也触发了,因此找到了视图组件的 Default.cshtml 文件。这个 Default.cshtml 有一些标记来呈现 table,在 table 中我有一个 @foreach() 语句,什么时候对那个精确位置执行 "Run to cursor" - 循环迭代 - 它也会触发,因此它会迭代整个集合。

但是毕竟主视图看起来好像不存在视图组件,Default.cshtml 中 HTML 的 none 即使找到了也会呈现并执行。我在这里错过了什么?到目前为止,我的印象是 VS.2017(及其所有更新)不是很 stable.

Default.cshtml

@using ACME.AspNetCore.Permissions.Infrastructure.Authorization
@model Dictionary<Permission, string>
@{ 
    ViewBag.UserName = "xxx";
    Model.Add(Permission.Permission1, "test");
}

<h1>Component Output</h1>
<div class="well well-lg">
<table class="table table-hover">
    <thead>
        <tr>
            <th>Permission</th>
            <th>Description</th>
            <th class="text-center">Status</th>
            <th class="text-center">Revoke it!</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var dictentry in Model)
        {
            <tr>
                <td>@dictentry.Key.ToString()</td>
                <td>@dictentry.Value</td>
                <td class="text-center"><span class="glyphicon glyphicon-ok" style="color:green;"></span></td>
                <td class="text-center"><a asp-action="RevokePermission" asp-route-id="@ViewBag.UserName" asp-route-pid="@dictentry.Key.ToString()"><span class="glyphicon glyphicon-thumbs-down" style="color:red;"></span></a></td>
            </tr>
        }
    </tbody>
    <tfoot><p class="alert alert-success"><span class="glyphicon glyphicon-eye-open"></span> Granted permissions</p></tfoot>
</table>
</div>

GrantedPermissionsViewComponent.cs

 [ViewComponent(Name = "GrantedPermissions")]
 public class GrantedPermissionsViewComponent : ViewComponent {
     private readonly ApplicationDbContext _context;
     public GrantedPermissionsViewComponent(ApplicationDbContext context) : base()
    {
        _context = context;
    }

    public async Task<IViewComponentResult> InvokeAsync(string emailOrUserId)
    {
        string id;
        Guid UID;
        if (Guid.TryParse(emailOrUserId, out UID))
        {   // THE PARAMETER WAS A GUID, THUS A USER ID FROM IDENTITY DATABASE
            id = emailOrUserId;
        }
        else
        {   // THE PARAMETER IS ASSUMED TO BE AN EMAIL/USERNAME FROM WHICH WE CAN DERIVE THE USER ID
            id = _context.Users.Where(u => u.Email == emailOrUserId.Trim()).Select(s => s.Id).FirstOrDefault();
        }

        Dictionary<Permission, string> granted = GetOwnerPermissions(id);
        return View(granted);            
    }

    private Dictionary<Permission, string> GetOwnerPermissions(string userId) 
    {
        Dictionary<Permission, string> granted;
        granted = _context.UserPermissions.Where(u => u.ApplicationUserId == userId)
                                                .Select(t => new { t.Permission })
                                                .AsEnumerable() // to clients memory
                                                .Select(o => new KeyValuePair<Permission, string>(o.Permission, o.Permission.Description()))
                                                .ToList()
                                                .ToDictionary(x => x.Key, x => x.Value);

        return granted;
    }
 }

那么到底为什么它会在组件代码和组件视图 (default.cshtml) 上触发,但它不会呈现其中的 HTML 代码?

主视图中的组件调用:

@{await Component.InvokeAsync<GrantedPermissionsViewComponent>(
    new { emailOrUserId = ViewBag.UserName });
}

注意

问题在于您调用 ViewComponent 的方式。

如果您使用 @{ ... },则意味着您要执行代码而不是渲染输出。

如果您使用圆括号而不是方括号,结果将被渲染到输出。 @( ... )

在你的情况下,你甚至不需要括号。 尝试调用它有以下内容:

@await Component.InvokeAsync("GrantedPermissions", new { emailOrUserId = ViewBag.UserName })

更多信息here

试试这个,你的里程可能会有所不同。 :)

@if (User.Identity.IsAuthenticated)
{

    <div>User: @User.Identity.Name</div>
    @(await Component.InvokeAsync("DefaultNavbar"));

    @RenderBody()
}
else
{
    <div> show public pages</div>
    @RenderBody()
}