【问题标题】:RuntimeBinderException when accessing dynamic anonymous type in view访问视图中的动态匿名类型时出现 RuntimeBinderException
【发布时间】:2013-11-25 00:40:33
【问题描述】:

我在学习/修补 asp.net 时遇到了一个奇怪的异常情况。

我正在尝试显示这样的部分视图:

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

当我尝试访问 Action 时

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

带有消息的 RuntimeBinderException

“object”不包含“Action”的定义

被抛出。
奇怪的是,这条线运行良好:

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

这种行为让我很困惑,我宁愿避免使用这种解决方法。另外我不认为问题出在anonymous types being internal,因为 VS2013 中 ASP.NET 项目的 MVC 模板成功地做到了这一点:

那么这里发生了什么?

【问题讨论】:

  • 这发生在我身上;它正在工作 - 然后它就停止了,没有充分的理由。我不知道为什么。就像您在另一条评论中提到的那样:具有匿名类型的弱类型视图 do 在其他地方工作,例如 Visual Studio 模板。问题是是什么让他们突然停止在这里工作。

标签: c# asp.net-mvc asp.net-mvc-3 dynamic anonymous-types


【解决方案1】:

这个问题的答案可以在这里找到:http://www.heartysoft.com/ashic/blog/2010/5/anonymous-types-c-sharp-4-dynamic

摘自优秀博文:

Anonymous Types are Internal

Model.Action 调用失败的原因是Model 的类型信息在运行时不可用。它不可用的原因是因为匿名类型不是公共的。当该方法返回该匿名类型的实例时,它返回一个引用匿名类型实例的 System.Object - 主程序无法获得信息的类型。动态运行时尝试在对象上查找名为Action 的属性,但无法从它拥有的类型信息中解析它。因此,它会引发异常。

【讨论】:

  • VS2013 MVC 模板为什么会起作用呢?请参阅我的问题中的屏幕截图。
  • 不可以了
【解决方案2】:

这里有一些使用反射的选项。在大多数情况下,性能应该可以忽略不计。

实用类

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 中使用更紧凑的部分调用

@{
  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");
}

【讨论】:

    【解决方案3】:

    那么这里发生了什么?

    您的局部视图是弱类型的。您没有@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" })
    

    【讨论】:

    • 感谢您的回答。创建视图模型确实解决了这个问题(动态模型没有;模型已经是动态的)。如果在那之前它仍然是最好的/唯一的答案,我会在几天后接受这个答案。但有一件事仍然困扰着我:为什么VS2013中的ASP.NET项目的MVC模板可以工作?它的局部视图也是弱类型的。事实上,我的部分观点只是它的一个调整版本。如果您可以在答案中添加解释,我会立即接受。
    • 虽然使用虚拟机可以避免/解决问题,但它并不能解释错误。默认情况下通常接受动态/匿名对象(不是对象)。匿名类型通常在部分视图中工作,但在某些(未知的情况下)它只是停止工作。我刚刚遇到了同样的问题,不得不更改它以使用强类型对象。正如卡巴拉所说,@model dynamic 的失败方式与原始方式相同(因为它已经期待动态)。
    • 您也可以使用反射,但这样做是错误的。您可以使用 T GetData(this object model,String key)... return model.GetType().GetProperty(key).GetValue(model) 在 Object 上添加扩展方法作为 T。作为静态实用程序方法会更好。老实说,性能打击并没有那么糟糕。 Helper.Data(模型,键)。
    猜你喜欢
    • 2011-07-04
    • 1970-01-01
    • 2023-03-05
    • 2011-11-17
    • 1970-01-01
    • 2013-02-07
    • 2010-09-28
    • 2013-12-24
    • 1970-01-01
    相关资源
    最近更新 更多