File: DynamicViewData.cs
Web Access
Project: src\src\Mvc\Mvc.ViewFeatures\src\Microsoft.AspNetCore.Mvc.ViewFeatures.csproj (Microsoft.AspNetCore.Mvc.ViewFeatures)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
using System.Dynamic;
using System.Linq;
using Microsoft.AspNetCore.Shared;
 
namespace Microsoft.AspNetCore.Mvc.ViewFeatures;
 
[DebuggerDisplay("Count = {ViewData.Count}")]
[DebuggerTypeProxy(typeof(DynamicViewDataDebugView))]
internal sealed class DynamicViewData : DynamicObject
{
    private readonly Func<ViewDataDictionary> _viewDataFunc;
 
    public DynamicViewData(Func<ViewDataDictionary> viewDataFunc)
    {
        ArgumentNullException.ThrowIfNull(viewDataFunc);
 
        _viewDataFunc = viewDataFunc;
    }
 
    private ViewDataDictionary ViewData
    {
        get
        {
            var viewData = _viewDataFunc();
            if (viewData == null)
            {
                throw new InvalidOperationException(Resources.DynamicViewData_ViewDataNull);
            }
 
            return viewData;
        }
    }
 
    // Implementing this function extends the ViewBag contract, supporting or improving some scenarios. For example
    // having this method improves the debugging experience as it provides the debugger with the list of all
    // properties currently defined on the object.
    public override IEnumerable<string> GetDynamicMemberNames()
    {
        return ViewData.Keys;
    }
 
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        ArgumentNullException.ThrowIfNull(binder);
 
        result = ViewData[binder.Name];
 
        // ViewDataDictionary[key] will never throw a KeyNotFoundException.
        // Similarly, return true so caller does not throw.
        return true;
    }
 
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        ArgumentNullException.ThrowIfNull(binder);
 
        ViewData[binder.Name] = value;
 
        // Can always add / update a ViewDataDictionary value.
        return true;
    }
 
    private sealed class DynamicViewDataDebugView(DynamicViewData dictionary)
    {
        private readonly ViewDataDictionary _dictionary = dictionary.ViewData;
 
        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        public DictionaryItemDebugView<string, object>[] Items => _dictionary.Select(pair => new DictionaryItemDebugView<string, object>(pair)).ToArray();
    }
}