簡體   English   中英

在視圖中訪問動態匿名類型時的RuntimeBinderException

[英]RuntimeBinderException when accessing dynamic anonymous type in view

我在學習/修補asp.net時遇到了一個奇怪的異常現象。

我試圖顯示這樣的局部視圖:

@Html.Partial("_PartialView", new { Action = "Foo" })

當我嘗試訪問Action時

// Throws Microsoft.Csharp.RuntimeBinder.RuntimeBinderException
string throwsException = Model.Action; 

帶有消息的RuntimeBinderExceptionis

'object'不包含'Action'的定義

被扔了。
奇怪的是這條線很好用:

// This line works fine
string works = ((Type)Model.GetType()).GetProperty("Action").GetValue(Model);

這種行為讓我很困惑,我寧願避免使用這種解決方法。 此外,我不認為問題是匿名類型是內部的,因為VS2013中的ASP.NET項目的MVC模板成功完成了此操作:

在此輸入圖像描述

那么這里發生了什么?

這個問題的答案可以在這里找到: http//www.heartysoft.com/ashic/blog/2010/5/anonymous-types-c-sharp-4-dynamic

從優秀的博客文章中拉出來:

Anonymous Types are Internal

調用Model.Action失敗的原因是Model的類型信息在運行時不可用。 它不可用的原因是因為匿名類型不公開。 當該方法返回該匿名類型的實例時,它返回一個System.Object,它引用一個匿名類型的實例 - 一個類型的信息不可用於主程序。 動態運行時嘗試在對象上查找名為Action的屬性,但無法從其擁有的類型信息中解析它。 因此,它會引發異常。

那么這里發生了什么?

你的部分視圖是弱類型的。 您沒有@model定義。 所以默認情況下它是object ,這顯然不具有Action特性。

解決此問題的正確方法是定義視圖模型:

public class MyViewModel
{
    public string Action { get; set; }
}

您的部分視圖將被強類型化為:

@model MyViewModel
@{
    string throwsException = Model.Action; 
}

哪個將通過主視圖傳遞:

@Html.Partial("_PartialView", new MyViewModel { Action = "Foo" })

另一種可能性(我個人不喜歡,因為它依賴於運行時綁定)是在局部視圖中使用動態模型:

@model dynamic
@{
    string throwsException = Model.Action; 
}

然后你可以在調用它時傳遞一個匿名對象:

@Html.Partial("_PartialView", new { Action = "Foo" })

以下是使用反射的一些選項。 在大多數情況下,性能應該可以忽略不計。

實用類

public static class ModelHelper
{
    public static T Data<T>( String key)  
    {
        var html = ((System.Web.Mvc.WebViewPage)WebPageContext.Current.Page).Html;
        var model = html.ViewData.Model;
        return (T)model.GetType().GetProperty(key).GetValue(model) ;
    }
}

view.cshtml

@(Html.Partial("partial",new { Id="InstructorId"}))

paritial.cshtml文件

@model dynamic 
@{ 
    var id = ModelHelper.Data<String>("Id");
}

在view.cshtml中使用更緊湊的parital調用

@{
  var instructorId = "InstructorId";
  var windowTitle = "Window Title";
  var editorPageUrl = "~/View/Editors/Instructors.chstml";
}

@(Html.Partial("partial",new { instructorId, windowTitle, editorPageUrl }))

將變量調整為在paritial.cshtml文件中獲取推斷的名稱

@model dynamic 
@{ 
    var id = ModelHelper.Data<String>("instructorId");
    var id = ModelHelper.Data<String>("windowTitle");
    var id = ModelHelper.Data<String>("editorPageUrl");
}

創建一個簡單的視圖模型類並不難,但我不喜歡一次只能用於傳遞數據的類,所以這對我有用。

您還可以擴展默認視圖庫

namespace SafetyPlus.Shell.Code
{
    public abstract class ExtendedPageBaseClass<TModel> : WebViewPage<TModel> where TModel : class
    {
        public T Data<T>(String key) 
        {
            return (T)Model.GetType().GetProperty(key).GetValue(Model);
        }

        public Object Data(String key) 
        {
            return Data<Object>(key);
        }
    }
}

在/Views/web.config中注冊基類

<pages pageBaseType="SafetyPlus.Shell.Code.ExtendedPageBaseClass">
  ...
</pages>

像這樣在部分視圖中獲取數據

@{ 
    var id = Data("Id");
    var idTyped = Data<String>("Id");
}

或者使用我建議的擴展方法。

namespace NotYourDefaultNamespace
{
    public static class ModelExtensions
    {
        public static T Data<T>( this Object model, String key)  
        {
            return (T)model.GetType().GetProperty(key).GetValue(model) ;
        }
    }
} 

這個選項真的不會給你帶來什么,因為我們從之前的實用方法中找到了模型。 電話會變成。

@{
    var id = Model.Data("Id");
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM