对所有MVC部分视图仅执行一次:Context.Items(线程)安全吗?

问题描述 投票:2回答:4

我有一个视图,其中包含通过局部视图渲染的项目列表。

我希望能够在局部视图中为整个页面仅添加一次javascript脚本。我读到可以在所有(部分)视图之间使用Context.Items共享数据(如布尔ScriptAlreadyIncluded)。

但是我可以依靠吗?还是我应该使用其他东西?


我当前的代码(最相关的局部视图是ITypedModel.cshtml和AFieldFormula_DirectFieldFormula.cshtm)

Index.cshtml

@model IEnumerable<MygLogWeb.Classes.MemberField>

@{
    Layout = "~/Views/Shared/_Layout.cshtml";

    var uid = Guid.NewGuid().ToString();
}

@using (Html.BeginForm("Index", "MemberField", FormMethod.Post, new { id = uid }))
{

    @Html.AntiForgeryToken()

    <table class="table none">
        <thead>
            <tr>
                <th>
                    @Html.DisplayNameFor(model => model.IsSystem)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.IsVirtual)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.Name)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.Formula)
                </th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            @Html.EditorFor(model => model)
        </tbody>
        <tfoot>
            <tr>
                <td colspan="5">
                    <input type="button" value="@Resources.Common.Buttons.add" />
                </td>
            </tr>
        </tfoot>
    </table>

    <input type="submit" value="@Resources.Common.Buttons.save"/>
}

<script type="text/javascript">
    new TableEdit("@uid");
</script>

EditorTemplates/MemberField.cshtml

@model MygLogWeb.Classes.MemberField

<tr>
    <td>
        @Html.DisplayFor(model => model.IsSystem)
        @Html.HiddenFor(model => model.IsSystem)
    </td>
    <td>
        @Html.DisplayFor(model => model.IsVirtual)
        @Html.HiddenFor(model => model.IsVirtual)
    </td>
    <td>
        @if (Model.IsSystem)
        {
            @Html.DisplayFor(model => model.Name)
            @Html.HiddenFor(model => model.Name)
        }
        else
        {
            @Html.TextBoxFor(model => model.Name, new { data_focus = "true" })
            @Html.ValidationMessageFor(model => model.Name)
        }
    </td>
    <td>
        @Html.EditorFor(model => model.Formula, "ITypedModel")
    </td>
    <td>
        @if (!Model.IsSystem)
        {
            <input type="button" value="@Resources.Common.Buttons.delete" data-code="delete" />
        }
    </td>
</tr>

EditorTemplates/ITypedModel.cshtml

...
@foreach (var item in items)
{
    <span data-refered-type="@item.Item.Value">
        @if (item.Type.IsSubclassOf(typeof(AFieldFormula)))
        {
            var newModel = item.Item.Selected ? Model : Activator.CreateInstance(item.Type);

            @Html.Partial("~/Views/MemberField/EditorTemplates/AFieldFormula_" + item.Type.Name + ".cshtml", newModel)
        }
    </span>
}

EditorTemplates/AFieldFormula_ConstantFormula.cshtml

@model MygLogWeb.Classes.ConstantFormula

<b>ConstantFormula</b>
@Html.EditorFor(model => model.Constant)
@Html.ValidationMessageFor(model => model.Constant)

EditorTemplates/AFieldFormula_DirectFieldFormula.cshtml

@model MygLogWeb.Classes.DirectFieldFormula

...Build a JSON dictionnary...

<b>DirectFieldFormula</b>
@Html.EditorFor(model => model.Field)
@Html.ValidationMessageFor(model => model.Field)

...Some controls using the JSON dictionnary...

每个AFieldFormula_DirectFieldFormula.cshtml视图的字典都是相同的,所以我只想对每个网页进行计算和显示一次。

c# asp.net-mvc-5
4个回答
2
投票

Context.Items(线程)安全吗?

否,因为HttpContext在文档中不是线程安全的

此类型的

任何公共静态(在Visual Basic中为共享)成员都是线程安全的。不保证任何实例成员都是线程安全的。

如果要一次渲染它,则可以在第一个渲染中的页面中添加一个隐藏字段,并在后续渲染中检查该字段是否存在。


根据您的评论,您应该能够使用ViewBag来维护各个局部视图的状态,并确保脚本仅被渲染一次

ITypedModel.cshtml

...
@foreach (var item in items)
{
    <span data-refered-type="@item.Item.Value">
        @if (item.Type.IsSubclassOf(typeof(AFieldFormula)))
        {
            var newModel = item.Item.Selected ? Model : Activator.CreateInstance(item.Type);
            @Html.Partial("~/Views/MemberField/EditorTemplates/AFieldFormula_" + item.Type.Name + ".cshtml", newModel, new ViewDataDictionary { { "vb", ViewBag }})
        }
    </span>
}

AFieldFormula_DirectFieldFormula.cshtml

@model MygLogWeb.Classes.DirectFieldFormula

@{ var vb = ((dynamic)ViewData["vb"]); }

@if (!vb.ScriptAlreadyIncluded)
{
    ...Build a JSON dictionnary...   
}

<b>DirectFieldFormula</b>
@Html.EditorFor(model => model.Field)
@Html.ValidationMessageFor(model => model.Field)

@if (!vb.ScriptAlreadyIncluded)
{
    ...Some controls using the JSON dictionnary...
}

@if (!vb.ScriptAlreadyIncluded)
{
    @{ vb.ScriptAlreadyIncluded = true; } // set the flag
}

这将在首次调用RenderPartial("AFieldFormula_DirectFieldFormula")时呈现脚本,然后将ViewBag.ScriptAlreadyIncluded属性设置为true。然后,在后续渲染中(在传递ViewBag时),此属性将跳过JS内容,因为已经设置了ScriptAlreadyIncluded


1
投票

Context.Items(线程)安全吗?

否,为James says in his answer

但是它一定是吗?如果您不执行任何异步处理,则您的请求将全部在同一线程上执行,因此线程安全性不是问题。

并且Context.Items旨在在请求处理管道的不同部分之间共享对象。


0
投票

基于@詹姆斯的想法和this post

我可以只使用:ViewContext.Controller.ViewBag,因为我整个页面只有一个Controller(我在那里不使用ChildActions。)>

它给出:

@model MygLogWeb.Classes.DirectFieldFormula

@{
    var GlobalViewBag = ViewContext.Controller.ViewBag;
    if (GlobalViewBag.DirectFieldFormula_ScriptAlreadyIncluded as bool? != true)
    {
        <script type="text/javascript">
            // ...
        </script>
    }

    GlobalViewBag.DirectFieldFormula_ScriptAlreadyIncluded = true;
}

...

0
投票

所以,这是一个古老的问题,但是我在尝试解决最近的问题时发现了它。

© www.soinside.com 2019 - 2024. All rights reserved.