【问题标题】:Can I pass an anonymous type to my ASP.NET MVC view?我可以将匿名类型传递给我的 ASP.NET MVC 视图吗?
【发布时间】:2010-09-18 10:22:24
【问题描述】:

我刚刚开始使用 ASP.NET MVC,因为它处于测试阶段。在我的代码中,我正在运行一个简单的 LINQ to SQL 查询来获取结果列表并将其传递给我的视图。这种事:

var ords = from o in db.Orders
           where o.OrderDate == DateTime.Today
           select o;

return View(ords);

但是,在我的视图中,我意识到我需要访问每个订单的客户姓名。我开始使用o.Customer.Name,但我相当确定这是为每个订单执行单独的查询(因为 LINQ 的延迟加载)。

减少查询数量的合乎逻辑的方法是同时选择客户名称。比如:

var ords = from o in db.Orders
           from c in db.Customers
           where o.OrderDate == DateTime.Today
               and o.CustomerID == c.CustomerID
           select new { o.OrderID, /* ... */, c.CustomerName };

return View(ords);

除了现在我的“ords”变量是匿名类型的 IEnumerable。

是否可以声明一个 ASP.NET MVC 视图,使其接受一个 IEnumerable 作为其视图数据,其中 T 由从控制器传递的内容定义,或者我必须定义一个具体类型来填充来自我的查询?

【问题讨论】:

    标签: asp.net-mvc


    【解决方案1】:

    您可以传递一个对象并使用反射来获得您想要的结果。查看 ObjectDumper.cs(包含在 csharpexamples.zip 中)作为示例。

    【讨论】:

    • 是的,我在想使用反射来破解匿名类型打开并将其视为 Dictionary 可能是唯一的解决方案。如果是这样,那就有点可惜了。
    【解决方案2】:

    如果我没记错的话,匿名类型会在编译时转换为强类型对象。不过,强类型对象是否对视图数据有效是另一个问题。

    【讨论】:

      【解决方案3】:

      这个post 展示了如何从方法中返回匿名类型,但它不能满足您的要求。

      另一种选择可能是将匿名类型转换为 JSON(JavaScriptSerializer 会这样做),然后将该 JSON 返回到视图,然后您将需要一些 jQuery 等来执行您喜欢的操作。

      我一直在使用 Linq 将我的数据“塑造”成我的视图所需的 JSON 格式,并取得了巨大的成功。

      【讨论】:

      • JSON 的想法听起来很有趣,但如果我要添加代码来实现它,那么我不妨定义一个包含我的订单和客户属性的类,并放弃匿名类型的想法。无论如何+1。
      • 同意。我的主要 JSON 使用场景是从用于填充网格(例如 Flexigrid)的 ajax 调用返回 JSON 对象,网格需要特定格式的数据,Linq 帮助我将数据塑造成该格式。
      【解决方案4】:

      你能把它传递给视图吗?是的,但您的视图不会是强类型的。但助手会工作。例如:

      public ActionResult Foo() {
        return View(new {Something="Hey, it worked!"});
      }
      
      //Using a normal ViewPage
      
      <%= Html.TextBox("Something") %>
      

      该文本框应该呈现“嘿,它成功了!”作为值。

      那么你能定义一个视图,其中 T 由控制器传递给它的内容定义吗?嗯,是的,但显然不是在编译时。

      想一想。当您为视图声明模型类型时,您会获得视图的智能感知。这意味着必须在编译时确定类型。但问题是,我们能否根据运行时给予它的东西来确定类型。当然可以,但没有保留强类型。

      如何为您还不知道的类型获取 Intellisense?控制器最终可能会在运行时将任何类型传递给视图。我们甚至无法分析代码和猜测,因为我们所知道的操作过滤器可能会更改传递给视图的对象。

      我希望能澄清答案,而不会造成更多混淆。 :)

      【讨论】:

      • 如果有人知道一定是你,菲尔。感谢像我这样的平民如此容易接近!
      • 这回答了问题吗?我有这个确切的问题,这个答案没有帮助。您能否展示我们如何循环遍历 ords 并在视图中显示?
      • 它没有回答问题!令人惊讶的是它适用于文本框,但如果我只想渲染这个值,我该怎么做? 不起作用,你会得到 'object' does not contain a definition for 'Something' 异常。
      • @Robert 也许是因为它回答了这个问题。问题是,你能不能把它传递给视图。是的。你能做到这一点,即视图的通用参数类型由控制器传递给它的内容决定吗?不,我会用更多细节更新我的答案。 :)
      • 在 ASP.NET MVC Futures 中还有另一种解决方案,称为 DynamicViewPage。 t.co/9RVYMBB
      【解决方案5】:

      不管怎样,今晚我发现了DataLoadOptions 类及其LoadWith 方法。我能够告诉我的 LINQ to SQL DataContext 在检索到 Orders 行时始终加载 Customers 行,因此原始查询现在可以一键获取我需要的所有内容。

      【讨论】:

      • 哇...非常感谢 - 在过去的几个小时里,我一直在疯狂地寻找这个。我知道它一定存在,但不知道去哪里找。谢谢大佬!
      【解决方案6】:

      您可以编写具有与匿名类型相同属性的类,并且可以将匿名类型转换为手写类型。缺点是在 linq 查询中进行投影更改时必须更新类。

      【讨论】:

      • 这完全违背了在操作方法中使用匿名类型的目的。到那时,你还不如只返回一个类实例。
      【解决方案7】:

      我也有同样的问题......经过一番思考,我得出的结论是,最正确和最具可扩展性的解决方案是在发送到视图之前序列化这个匿名类型。因此,您可以使用相同的方法使用后面的 View 代码填充页面并使用 JSON 填充页面

      【讨论】:

        【解决方案8】:

        可以将匿名类型传递给视图,只需记住将模型转换为动态。

        你可以这样做:

        return View(new { 
            MyItem = "Hello", 
            SomethingElse = 42, 
            Third = new MyClass(42, "Yes") })
        

        然后您可以在视图顶部执行此操作(在此处使用剃刀)

        @{
            string myItem = (dynamic)Model.MyItem;
            int somethingElse = (dynamic)Model.SomethingElse;
            MyClass third = (dynamic)Model.Third;
        }
        

        或者您可以像这样从 ViewData 中投射它们:

        @{
            var myItem = ViewData.Eval("MyItem") as string
            var somethingElse = ViewData.Eval("SomethingElse") as int?
            var third = ViewData.Eval("Third") as MyClass 
        }
        

        【讨论】:

        • 好吧,你的第一个例子不起作用。至少在 MVC3 中没有。惊喜!首先,在无类型视图中,Model 已经是dynamic,无需强制转换。其次,使用您的代码,我得到一个RuntimeBinderException,其中包含消息“'object' 不包含 MyItem 的定义”。有什么想法吗?
        • 我刚刚找到了一个解释。事实证明,匿名类型是内部的,这意味着它们的属性在它们定义的程序集之外是看不到的。看这里:gregshackles.com/2010/09/…
        • 在 asp.net core .net5 中:Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:“object”不包含“myItem”的定义
        • 过去 10 年我没有使用 .NET,所以我无法回答为什么这不适用于 .NET5 Core:P
        【解决方案9】:

        在 .NET 4.0 上,匿名类型可以很容易地转换为 ExpandoObjects,因此所有问题都可以通过转换本身的开销来解决。 查看here

        【讨论】:

          【解决方案10】:

          Here 是一篇解释如何将匿名类型传递给视图并绑定数据的文章。

          谢谢

          【讨论】:

            【解决方案11】:

            记住:anonymous 类型是内部的,这意味着它们的属性不能在其定义的程序集之外看到。

            您最好将anonymous 类型转换为dynamic,使用扩展方法将dynamic 对象(而不是anonymous 一个)传递给您的View

            public class AwesomeController : Controller
            {
                // Other actions omitted...
                public ActionResult SlotCreationSucceeded(string email, string roles)
                {
                    return View("SlotCreationSucceeded", new { email, roles }.ToDynamic());
                }
            }
            

            扩展方法如下所示:

            public static class DynamicExtensions
            {
                public static dynamic ToDynamic(this object value)
                {
                    IDictionary<string, object> expando = new ExpandoObject();
            
                    foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(value.GetType()))
                        expando.Add(property.Name, property.GetValue(value));
            
                    return (ExpandoObject) expando;
                }
            }
            

            尽管如此,您仍然可以传递anonymous 对象,但您必须稍后将其转换为dynamic

            public class AwesomeController : Controller
            {
                // Other actions omitted...
                public ActionResult SlotCreationSucceeded(string email, string roles)
                {
                    return View("SlotCreationSucceeded", new { email, roles });
                }
            }
            

            查看:

            @{
                var anonymousModel = DynamicUtil.ToAnonymous(Model, new { email = default(string), roles = default(string) });
            }
            
            <h1>@anonymousModel.email</h1>
            <h2>@anonymousModel.roles</h2>
            

            辅助方法如下所示:

            public class DynamicUtil
            {
                public static T ToAnonymous<T>(ExpandoObject source, T sample)
                    where T : class
                {
                    var dict = (IDictionary<string, object>) source;
            
                    var ctor = sample.GetType().GetConstructors().Single();
            
                    var parameters = ctor.GetParameters();
            
                    var parameterValues = parameters.Select(p => dict[p.Name]).ToArray();
            
                    return (T) ctor.Invoke(parameterValues);
                }
            }
            

            【讨论】:

            • 我已经尝试过了,但它一直给出 RuntimeBinderException 错误。
            猜你喜欢
            • 1970-01-01
            • 2016-06-01
            • 2011-05-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多