ASP.NET MVC ViewData.Eval() 方法

本文深入探讨了ASP.NET MVC中ViewData.Eval方法的功能与使用技巧,包括如何通过该方法检索视图数据对象图中的信息,以及如何自定义其支持索引的能力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

From【 http://weblogs.asp.net/nmarun/archive/2009/11/26/asp-net-mvc-viewdata-eval-method.aspx

While digging deeper into MVC Views, I stumbled on this method – ViewData.Eval(). Found it interesting and researched and played with some code around this method.

This method gives the user a way to search through the ViewData’s object graph. So in order to get the data, you can either do

1 <%= ViewData[ " Message " ] %>

or

1 <%= ViewData.Eval( " Message " ) %>
 
   

Either line gives the same answer. The key passed, “Message” is case-insensitive => “Message” = “message” = “mEssage” and so on.

You can walk through the graph using the ‘.’ (dot) syntax. So you can do

1 ViewData[ " employee " ] = new Employee{ Name = " ScottGu " };
2
3   <%= ViewData.Eval( " Employee.Name " ) %>
 
    

MVC recursively loops through the graph looking for the whole key first and then removing one token at a time. So in the above case, it looks for the whole string ‘Employee.Name’ first, which returns a null. Then it tries to search the graph for ‘Employee’, which it does find and then it looks for ‘Name’ in the Employee graph.

It first checks in the ViewData’s dictionary and then tries to find it in the ViewData.Model’s properties. To test this I created a class called Software to represent my model.

1 public class Software
2 {
3 public string Name { get ; set ; }
4 public string Version { get ; set ; }
5 public string Company { get ; set ; }
6 }
 
   

In the HomeController’s Index action, I added:

1 ViewData[ " software " ] = new Software
2 {
3 Name = " MVC " ,
4 Version = " 2 " ,
5 Company = " MSFT " ,
6 };
7 ViewData[ " software.name " ] = " test " ;
 
   

And accordingly, in the View, the code looks like:

1 <%= ViewData.Eval( " software.name " ) %>
 
   

When you run the app, you’ll see ‘test’ getting rendered for the above line instead of ‘MVC’, which, in this case, also maps to ‘software.name’ in the ViewData’s object graph. Remove line 7 from the above code and you’ll see ‘MVC’ gets rendered. This proves the first point. Although it’s very rare to hit this situation, it is good to know.

You can also use Eval to fill-in values for controls in an Edit screen. Here's a very interesting read regarding this from Stephen Walther. In this article, Stephen says “When you need to repopulate the form data in an edit form, displaying both valid and invalid values, use ViewData.Eval() to retrieve the values from both the view data dictionary and the view data Model.

It’s interesting to know that there is no support for finding indexed items in an array through the Eval method. So if your controller has something like below:

   1:  List<Software> softwares = new List<Software>();
   2:  softwares.Add(new Software
   3:                    {
   4:                        Name = "ASP.NET MVC",
   5:                        Version = "2",
   6:                        Company = "MSFT"
   7:                    });
   8:   
   9:  softwares.Add(new Software
  10:                    {
  11:                        Name = "C#",
  12:                        Version = "4.0",
  13:                        Company = "MSFT",
  14:                    });
  15:   
  16:  ViewData["Softwares"] = softwares;
  17:   
  18:  // and your view said the following
  19:  <%= ViewData.Eval("softwares[0].name") %>
  20:  // this returns null

I did a search to see if Eval could handle indexes and I hit one interesting link. According to Phil (sounds like According to Jim) Haack, Eval does not support index syntax and he suggests to try:

1 <%= ViewData.Eval( " softwares.0.name " ) %>

This did not work for me. I even looked at the source code and there’s nothing in there that can catch the ‘.0’ and enumerate through the ‘softwares’ collection and pull out the first item (index = 0). And also, this syntax will not come instinctively to either a C# or a VB.net developer.

I decided to see if I could add that functionality to the Eval method. Since MVC is Open Source, you have the ability to look and modify (at your own risk) the code. (See my article Custom 404 when no route matches, where I’ve modified the MVC source code before.)

I landed in the ViewDataDictionary.cs class (the other class with a very similar name takes a generic parameter – ignore that). In this class, there is an internal class named ‘ViewDataEvaluator’ which defines the Eval method. The first thing I did was to move this into a .cs file of its own, just to add more clarity. Well, it does not make sense for me to go through all the lines of code in this class, but the method that caught my attention was the GetIndexedPropertyValue. It’s default implementation looks like this:

1 private static object GetIndexedPropertyValue( object indexableObject, string key) {
2 Type indexableType = indexableObject.GetType();
3
4 ViewDataDictionary vdd = indexableObject as ViewDataDictionary;
5 if (vdd != null ) {
6 return vdd[key];
7 }
8
9 MethodInfo containsKeyMethod = indexableType.GetMethod( " ContainsKey " ,
10 BindingFlags.Public | BindingFlags.Instance,
11 null ,
12 new Type[] { typeof ( string ) },
13 null );
14 if (containsKeyMethod != null ) {
15 if ( ! ( bool )containsKeyMethod.Invoke(indexableObject, new object [] { key })) {
16 return null ;
17 }
18 }
19
20 PropertyInfo info = indexableType.GetProperty( " Item " ,
21 BindingFlags.Public | BindingFlags.Instance,
22 null ,
23 null ,
24 new Type[] { typeof ( string ) },
25 null );
26 if (info != null ) {
27 return info.GetValue(indexableObject, new object [] { key });
28 }
29
30 PropertyInfo objectInfo = indexableType.GetProperty( " Item " ,
31 BindingFlags.Public | BindingFlags.Instance,
32 null ,
33 null ,
34 new Type[] { typeof ( object ) },
35 null );
36 if (objectInfo != null ) {
37 return objectInfo.GetValue(indexableObject, new object [] { key });
38 }
39 return null ;
40 }
 
   

With a little hassle, I was (partially) able to make the Eval method support indexing. Replace the below code in place of the code between lines 4 through 7 of the above snippet.

1 ViewDataDictionary vdd = indexableObject as ViewDataDictionary;
2 if (vdd != null )
3 {
4 // the below code helps to decode keys like: softwares[0].name
5 // there's no in-built support for indexing in the Eval method
6 // this 'detour' checks for indexes in keys
7 // and returns the appropriate object value
8 // it is worth mentioning that the indexing
9 // is done only on the first token
10 // – ‘softwares’ token as per this example
11
12 // check if the key has '[' and it does not have any other tokens
13 // i.e., key = softwares[0] and not like key = softwares[0].name
14 if (key.Contains( " [ " ) && ! key.Contains( " . " ))
15 {
16 // get the actual key (softwares, in this case)
17 string actualKey = key.Substring( 0 , key.IndexOf( " [ " ));
18
19 // store the index value
20 int index = int .Parse(key.Replace(actualKey, "" ).Replace( " [ " , "" ).Replace( " ] " , "" ));
21
22 // get the collection of vdd[actualKey]
23 var objectList = vdd[actualKey];
24
25 if (objectList != null )
26 {
27 // make sure object can be enumerated
28 if (objectList is IEnumerable)
29 {
30 int counter = 0 ;
31 // create a dummy return item object
32 // this avoids checking if index is out of bounds of objectList
33 // if index > number of items in objectList
34 // then return null be default
35 object returnItem = null ;
36
37 foreach (var item in (IEnumerable)objectList)
38 {
39 // return the item that matches the index
40 if (index == counter)
41 {
42 returnItem = item;
43 break ;
44 }
45 // increment the counter to check
46 // against the next index
47 counter ++ ;
48 }
49 return returnItem;
50 }
51 }
52 }
53 else
54 {
55 return vdd[key];
56 }
57 }

I believe, I’ve added enough comments to make the code easy to understand and so I will not go through it line by line. But there’s one thing I’d like to repeat. This code works only if the first token needs to be indexed. In our case, this means we’ll be able to retrieve the value for ‘softwares[0].name’. It will not work for something like ‘softwares[1].previousversions[0].name’ (if previousversions was some collection with a property called ‘name’ in the ‘Software’ class).

All coding/testing for this article has been done on VS2008 and MVC1. I wanted to test this on VS2010 with MVC2, but then again, Phil Haack says, ‘running ASP.NET MVC 2 Beta on VS10 Beta 2 is not supported’. I also did go through the Release Notes and I did not see any changes for the ViewData.Eval method. So hopefully the above article holds good for MVC 2 as well. Happy to be proven wrong.

Published Thursday, November 26, 2009 12:39 AM by  nmarun
Filed under: C#ASP.NET MVC

转载于:https://www.cnblogs.com/jacobz/archive/2011/06/26/2090703.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值