使用泛型和LINQ的MVC3 HtmlHelpers

时间:2011-06-29 16:40:19

标签: c# linq asp.net-mvc-3 html-helper

已更新

我正在尝试创建一个能够获取TModels集合的HtmlHelper扩展。然后设置一个Expression,它将获取集合中声明的属性foreach项并将其打印出来。

下面的代码可行。但是,我目前所拥有的模式中我不喜欢的一件事是不推断泛型类型。我必须为我的扩展方法声明类型。

我正在尝试遵循Telerik用于其Grid的模式类型,当您声明模型时,会推断出泛型类型。

我想在我看来这样做:

@model List<MyProject.MyModels.UserModel>
@(Html.MyExtensions()
      .PrintUsers(Model)
      .FirstName(m => m.FirstName)
      .LastName(m => m.LastName)
)

根据我目前的模式,我必须这样做:

@model List<MyProject.MyModels.UserModel>
@(Html.MyExtensions<List<MyProject.MyModels.UserModel>, MyProject.MyModels.UserModel>()
      .PrintUsers(Model)
      .FirstName(m => m.FirstName)
      .LastName(m => m.LastName)
)

所以我需要找出一个更好的模式,以便推断出我的类型。 有什么想法吗?

UserModel看起来像:

public class UserModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

所以我有HtmlHelper看起来像这样:

public static class UIExtension
{
    public static ComponentFactory<TModel, T> MyExtensions<TModel, T>(this HtmlHelper<TModel> html) 
        where TModel : IEnumerable<T>
        where T : class
    {
        return new ComponentFactory<TModel, T>(html);
    }
}

因此,我的一个组件将获取一个List,然后通过将每个声明的属性从Linq Expression打印到页面来进行迭代。

在我的ComponentFactory我有这个:

public class ComponentFactory<TModel, T>
    where T : class
{
    private HtmlHelper<TModel> _html;

    public ComponentFactory()
    {
    }

    public ComponentFactory(HtmlHelper<TModel> html)
    {
        this._html = html;
    }

    public ListComponent<T> PrintUsers(IEnumerable<T> model)
    {
        return new ListComponent<T>(model);
    }

    /* Add other ui components to this factory */
}

然后我的ListComponent

public class ListComponent<T> : IHtmlString
{
    private Expression<Func<T, string>> _firstname;
    private Expression<Func<T, string>> _lastname;

    private IEnumerable<T> _model;

    public ListComponent(IEnumerable<T> model)
    {
        this._model = model;
    }

    public ListComponent<T> FirstName(Expression<Func<T, string>> property)
    {
        this._firstname = property;
        return this;
    }

    public ListComponent<T> LastName(Expression<Func<T, string>> property)
    {
        this._lastname = property;
        return this;
    }

    public override MvcHtmlString Render()
    {
        TagBuilder container = new TagBuilder("div");

        TagBuilder title = new TagBuilder("div");
        title.InnerHtml = "<b>Names</b>";
        container.InnerHtml = title.ToString(TagRenderMode.Normal);

        TagBuilder ul = new TagBuilder("ul");

        foreach (var item in this._model)
        {
            TagBuilder li = new TagBuilder("li");
            li.SetInnerText(String.Format("{0} {1}", _firstname.Compile()(item), _lastname.Compile()(item)));
            ul.InnerHtml += li.ToString(TagRenderMode.Normal);
        }

        container.InnerHtml += ul.ToString(TagRenderMode.Normal);

        return MvcHtmlString.Create(container.ToString(TagRenderMode.Normal));
    }
}

感谢您提供任何帮助和建议!

3 个答案:

答案 0 :(得分:2)

这样的事情:

public static class UIExtension
{
    public static ComponentFactory<TModel> MyExtensions<TModel>(this HtmlHelper<IEnumerable<TModel>> html) where TModel : class

    {
        return new ComponentFactory<TModel>(html);
    }
}
public class UserModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
public class ComponentFactory<TModel>
where TModel : class
{
    private HtmlHelper _html;

    public ComponentFactory()
    {
    }

    public ComponentFactory(HtmlHelper<IEnumerable<TModel>> html)
    {
        this._html = html;
    }

    public ListComponent<TModel> PrintUsers(IEnumerable<TModel> model)
    {
        return new ListComponent<TModel>(model);
    }

    /* Add other ui components to this factory */
}
public class ListComponent<T> : IHtmlString
{
    private Expression<Func<T, string>> _firstname;
    private Expression<Func<T, string>> _lastname;

    private IEnumerable<T> _model;

    public ListComponent(IEnumerable<T> model)
    {
        this._model = model;
    }

    public ListComponent<T> FirstName(Expression<Func<T, string>> property)
    {
        this._firstname = property;
        return this;
    }

    public ListComponent<T> LastName(Expression<Func<T, string>> property)
    {
        this._lastname = property;
        return this;
    }



public override MvcHtmlString Render()
{
    TagBuilder container = new TagBuilder("div");

    TagBuilder title = new TagBuilder("div");
    title.InnerHtml = "<b>Names</b>";
    container.InnerHtml = title.ToString(TagRenderMode.Normal);

    TagBuilder ul = new TagBuilder("ul");

    foreach (var item in this._model)
    {
        TagBuilder li = new TagBuilder("li");
        li.SetInnerText(String.Format("{0} {1}", _firstname.Compile()(item), _lastname.Compile()(item)));
        ul.InnerHtml += li.ToString(TagRenderMode.Normal);
    }

    container.InnerHtml += ul.ToString(TagRenderMode.Normal);

    return MvcHtmlString.Create(container.ToString(TagRenderMode.Normal));
}
}

用法如下:

Html.MyExtensions().PrintUsers(Model).FirstName(p=>p.FirstName).LastName(p=>p.LastName)

答案 1 :(得分:1)

在看了@Tomas的回答之后,它帮助我意识到我在模式中缺少的东西。虽然@Tomas的回答确实让我在View中得到了正确推断Model的语法,但唯一的缺点是整个ViewModel被绑定到{IEnumerable<T> 1}}。因为我想绑定到我的ViewModel中属于IEnumerable的属性,所以我进行了一些更改,然后就解决了。

HtmlHelper扩展程序:

public static class UIExtension
{
   public static ComponentFactory MyExtensions(this HtmlHelper html)
   {
       return new ComponentFactory(html);
   }

   public static ComponentFactory<TModel> MyExtensions<TModel>(this HtmlHelper<TModel> html) 
      where TModel : class
   {
       return new ComponentFactory<TModel>(html);
   }
}

ComponentFactory

public class ComponentFactory<TModel>
{
   private HtmlHelper<TModel> _html;
   public ComponentFactory(HtmlHelper<TModel> html)
   {
      this._html = html;
   }

   public ListComponent<TObj> PrintUsers<TObj>(IEnumerable<TObj> model)
   {
      return new ListComponent<TObj>(model);
   }
}

所以主要的变化是对于PrintUsers方法,我使用不同的通用类型来处理该部分。这样我的Model可以是开发人员决定的内容,他们只需将Property设置为datasource的{​​{1}}。

所以现在在ListComponent它非常灵活,我可以做这样的事情......

View

答案 2 :(得分:0)

您可以使用两个类型参数,一个用于列表,另一个用于项目类型:

ListComponent<TList, TItem> Print<TList, TItem>(TList list) where TList : IEnumerable<TItem>

更新: 也许通过这种方式,类型推断将起作用:

ListComponent<TItem> Print<TItem>(IEnumerable<TItem> list)