简体   繁体   中英

How does Html.EditorFor(…) know the index and item of a loop?

Suppose you have a model like so:

class Foo
{
    public int A {get; set;}
    public int B {get; set;}
}

class SomeModel
{
    public List<Foo> Foos { get; set; }

}

In a razor view on the ASP.NET mvc framework, you can do the following:

@model SomeModel

@for(int i = 0; i < Model.Foos.Count; ++i)
{
   Html.EditorFor(x => x.Foos[i]);
}

And the razor engine will happily spit out the correct html containing the index, and will call the editor template with the correct indexed instance.

The EditorFor method is a static extension method with the signature

public static MvcHtmlString EditorFor<TModel, TValue>(
this HtmlHelper<TModel> html,
Expression<Func<TModel, TValue>> expression
)

From the signature, it is clear that it is simply taking an expression, and the only context is coming from the HtmlHelper instance.

I have done very limited Expression tree processing, but from what I have seen, I can't see any way that this static method could know the information that it is somehow magically getting.

How can the EditorFor method figure out the index for generating html names and get the correct instance to pass to the editor template?

You are passing it an expression, not the value of x.Foos[i] . MVC then evaluates that expression and figures out that you gave it a collection with an index. You would get the same result if you removed your entire loop and did:

Html.EditorFor(x => x.Foos)

MVC will then automatically render the editor template for all elements of the collection and generate proper names.

You can read more about how MVC handles display/editor templates here: Link

EDIT : To see this in action, here's a random piece of code I scribbled:

List<string> list = new List<string>() { "A", "B", "C" };
var tester = new ExpressionTester<List<string>>(list);
var item = tester.Foo(p => p[0]);

You'll also need this class:

public class ExpressionTester<TModel>
{
    private TModel _list;
    public ExpressionTester(TModel list)
    {
        _list = list;
    }

    public TValue Foo<TValue>(Expression<Func<TModel, TValue>> expression)
    {
        var func = expression.Compile();
        return func.Invoke(_list);
    }
}

Stick a breakpoint in Foo() and look at the parameter expression in debug. You'll find under Body -> Arguments the index you passed with the expression. Under Body -> Method you'll see that it is in fact a generic list.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM