Цепочка лямбда-выражений в MVC.Net без повторной передачи объекта HtmlHelper

Моя цель — создать объект, позволяющий связывать команды в представлениях MVC.Net.

Вот пример использования в представлении меню, которое я создал с использованием этой концепции:

<nav class="navigation">
    <%: Html
        .menu()
            .item("Introduction", "Introduction", "Home")
            .item("About", "About", "Home")
            .item("Systems", "Index", "Systems")
            /*.item("Categories", "Categories", "Health")*/
            .item("Test Cases", "TestCases", "Testing")
            .category("Logging")
                .item("UniMon Events", "UniMonEvents", "Logging")
            .end()
        .end() %>
</nav>

Как видите, это позволяет быстро создавать многоуровневое меню с взаимозависимостью между различными частями.

Я хотел бы добиться такого же эффекта для формы с использованием лямбда-выражений.

Идеальный синтаксис будет выглядеть так:

<%: Html
    .form()
        .hidden(m=>m.property1)
        .hidden(m=>m.property2)
    .end() %>

У меня возникают проблемы с скрытым методом. Кажется, нет способа заставить компилятор вывести m, не передав его методу hidden.

Я могу добиться этого синтаксиса:

<%: Html
    .form()
        .hidden(Html, m=>m.property1)
        .hidden(Html, m=>m.property2)
    .end() %>

Используя этот класс и метод расширения (не показан):

public class RouteForm
{
    public HtmlHelper HtmlHelper { get; private set; }
    public Dictionary<string, string> PostData { get; private set; }

    public RouteForm(HtmlHelper htmlHelper)
    {
        HtmlHelper = htmlHelper;
        PostData = new Dictionary<string, string>();
    }

    public RouteForm hidden<TModel, TValue>(HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression)
    {
        string name = ExpressionHelper.GetExpressionText(expression);
        string value = GetFieldValue(htmlHelper, expression);
        PostData.Add(name, value);
        return this;
    }
    private static string GetFieldValue<TModel, TValue>(HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression)
    {
        object oValue = expression.Compile()(htmlHelper.ViewData.Model);
        string value = (oValue is Enum) ? ((int)oValue).ToString() : oValue.ToString();
        return value; ;
    }
    public MvcHtmlString end()
    {
        //TODO: render form with post data
        return MvcHtmlString.Empty;
    }
}

Я подумал, что, возможно, класс с универсальным типом может быть тем, что я ищу, поэтому я попробовал это:

public class RouteForm<TModel>
{
    public HtmlHelper<TModel> HtmlHelper { get; private set; }
    public Dictionary<string, string> PostData { get; private set; }

    public RouteForm(HtmlHelper<TModel> htmlHelper)
    {
        HtmlHelper = htmlHelper;
        PostData = new Dictionary<string, string>();
    }

    public RouteForm<TModel> hidden<TModel, TValue>(Expression<Func<TModel, TValue>> expression)
    {
        string name = ExpressionHelper.GetExpressionText(expression);
        string value = GetFieldValue(expression);
        PostData.Add(name, value);
        return this;//ERRORS: TModel is TModel
    }
    private string GetFieldValue<TModel, TValue>(Expression<Func<TModel, TValue>> expression)
    {
        object oValue = expression.Compile()(
            (TModel)HtmlHelper.ViewData.Model //ERRORS: Cannot convert type TModel to TModel
        );
        string value = (oValue is Enum) ? ((int)oValue).ToString() : oValue.ToString();
        return value; ;
    }
    public MvcHtmlString end()
    {
        //TODO: render form with post data
        return MvcHtmlString.Empty;
    }
}

Я помещаю ошибки в код выше, используя комментарии.

Спасибо!


person N-ate    schedule 22.02.2013    source источник


Ответы (3)


Вы используете слишком много общих параметров.

Такие методы, как GetFieldValue<TModel, ...>, создают второй параметр TModel, не связанный с первым.

Другими словами, они позволяют вам писать

new RouteForm<PersonModel>().GetFieldValue<TruckModel, ...>()

Это явно неправильно.

Вместо этого просто избавьтесь от этого параметра из каждого метода и позвольте им вместо этого использовать параметр TModel класса.

person SLaks    schedule 22.02.2013
comment
Сделанный. Это было совершенно очевидно, как только вы указали на это. Теперь я могу написать: new mvc.RouteForm‹CurrentModel›(Html).hidden(m =› m.Property); - person N-ate; 22.02.2013
comment
Есть ли способ заставить компилятор вывести тип из конструктора, а не передавать его явно? Например, новый mvc.RouteForm(Html).hidden(m => m.Property); Учитывая конструктор: public RouteForm(HtmlHelper‹TModel› htmlHelper) - person N-ate; 22.02.2013
comment
@Натаниэль: Нет; вывод типа не применяется к конструкторам. Вместо этого используйте метод расширения. - person SLaks; 22.02.2013

Я предполагаю, что ошибка компиляции «ОШИБКИ: TModel is TModel» вызвана двойным объявлением TModel в общем объявлении hidden ().

Я не компилировал это, но я бы попробовал что-то вроде этого:

public static class HtmlHelperExtensions
{
    public static RouteForm<TModel> form(this HtmlHelper helper, TModel model)
    {
        return new RouteForm<TModel>(helper);
    }
}

public class RouteForm<TModel>
{
    public RouteForm<TModel> hidden(Expression<Func<TModel, TValue>> expression)
    {
    }        
    public MvcHtmlString end()
    {
    }
}
person devio    schedule 22.02.2013

Благодаря вам обоим я смог создать класс, который реализует синтаксис, который я искал.

(для этого поста класс упрощен) класс:

public class RouteForm<TModel>
{
    public HtmlHelper<TModel> HtmlHelper { get; private set; }

    public RouteForm(HtmlHelper<TModel> htmlHelper)
    {
        HtmlHelper = htmlHelper;
    }

    public RouteForm<TModel> hidden<TValue>(Expression<Func<TModel, TValue>> expression)
    {
        return this;
    }
    public MvcHtmlString end()
    {
        return MvcHtmlString.Empty;
    }
}

метод расширения:

public static RouteForm<TModel> form<TModel>(this HtmlHelper<TModel> helper)
{
     return new RouteForm<TModel>(helper);
}

синтаксис разметки:

<%: Html
    .form()
        .hidden(m=>m.Property1)
        .hidden(m=>m.Property2)
    .end()
%>
person N-ate    schedule 22.02.2013