Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do it only once for all MVC partial view: Is Context.Items (thread) safe?

I have a View which hold a list of items rendered through partials views.

I'd like to be able in the partial view to add a javascript script only once for the whole page.
I read I could use Context.Items to share data (like a boolean ScriptAlreadyIncluded) amongst all (partial) views.

But can I rely on this? Or should I use something else?


My current code (most relevant partial view being ITypedModel.cshtml and 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...

The dictionary would be the same for every AFieldFormula_DirectFieldFormula.cshtml view, so I'd like to compute and display it only once per web page.

like image 735
Serge Avatar asked Oct 22 '25 22:10

Serge


2 Answers

Is Context.Items (thread) safe?

No, because HttpContext isn't thread safe, from the docs

Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

If you want it to be rendered once you could add a hidden field into the page on the first render and check for the existence of that on subsequent renders.


Based on your comments, you should be able to use the ViewBag to mainain state across the various partial views and ensure the script is only rendered once

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
}

This will render the script on the first call to RenderPartial("AFieldFormula_DirectFieldFormula") which then sets the ViewBag.ScriptAlreadyIncluded property to true. Then on subsequent renders (as the ViewBag is being passed in) this property will skip the JS stuff because ScriptAlreadyIncluded has already been set.

like image 140
James Avatar answered Oct 24 '25 13:10

James


Is Context.Items (thread) safe?

No, as James says in his answer.

But does it need to be? If you're not doing any asynchronous processing, your request will all execute on the same thread, so thread-safety is not an issue.

And Context.Items is designed to share objects between different parts of the request processing pipeline.

like image 24
Joe Avatar answered Oct 24 '25 11:10

Joe