📜 ⬆️ ⬇️

Passing anonymous objects to the View


The idea is to use the model with a new, more convenient dynamic syntax. The main limitation here is that you cannot simply pass an anonymous object as a model, because anonymous types have an access modifier internal.


Suppose we are trying to write this code in the controller:

public class HomeController : Controller
{
public ActionResult Index()
{
return View(
new
{
Message = "Welcome to ASP.NET MVC!" ,
Date = DateTime .Now
});
}
}

* This source code was highlighted with Source Code Highlighter .

')
Notice that we pass an anonymous object to the model. The main reason for this is to avoid creating the required external ViewModel type. Obviously, this is a somewhat controversial point, but it should look simpler than such an alternative:

ViewData[ "Message" ] = "Welcome to ASP.NET MVC!" ;
ViewData[ "Date" ] = DateTime .Now;
return View();

* This source code was highlighted with Source Code Highlighter .


Then we modify the view so that it has
Inherits= "System.Web.Mvc.ViewPage<dynamic>"

* This source code was highlighted with Source Code Highlighter .


Ideally, this should allow us to write something like this:

< asp:Content ID ="indexContent" ContentPlaceHolderID ="MainContent" runat ="server" >
< h2 > <% = Model.Message %> </ h2 >
< p >
The date is <% = Model.Date %>
</ p >
</ asp:Content >

* This source code was highlighted with Source Code Highlighter .


But by default dynamic binder will not let us do it!

Unfortunately, if you try to run this code, it will be:
error: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: '<> f__AnonymousType1 <system.DateTime> .Message' is inaccessible due to its protection level
The reason for this anonymous type in the controller is internal, so that it can only be accessed from the assembly in which it is defined. Since the view is compiled separately, dynamic binder complains that it cannot go beyond this assembly. But you can simply write your own DynamicObject, which binds through private reflection.

Here we are going to write not only our DynamicObject, but also our DynamicViewPage, which uses it. Here is the full implementation:

public class DynamicViewPage : ViewPage {
// dynamic
public new dynamic Model { get ; private set ; }

protected override void SetViewData(ViewDataDictionary viewData) {
base .SetViewData(viewData);

// , private
Model = new ReflectionDynamicObject() { RealObject = ViewData.Model };
}

class ReflectionDynamicObject : DynamicObject {
internal object RealObject { get ; set ; }

public override bool TryGetMember(GetMemberBinder binder, out object result) {
//
result = RealObject.GetType().InvokeMember(
binder.Name,
BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
null ,
RealObject,
null );

// true, InvokeMember , -
return true ;
}
}
}

* This source code was highlighted with Source Code Highlighter .


As you can see, everything is simple. All we need to do is find out the value of the property using private reflection. That's all, it remains only to use it as our base class for View.
<% @ Page Language = "C #" MasterPageFile = "~ / Views / Shared / Site.Master" Inherits = "MvcHelpers.DynamicViewPage"%>

Source: https://habr.com/ru/post/79363/


All Articles