有趣(?)与扩展方法LINQ的防爆pressions [英] Fun (?) with Linq Expressions in extension methods

查看:127
本文介绍了有趣(?)与扩展方法LINQ的防爆pressions的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一个前的HtmlHelper pression我用了很多的时间把标题标签到我的下拉列表,像这样:

 公共静态HtmlString SelectFor<的TModel,TProperty,TListItem>(
        这与的HtmlHelper LT;的TModel>的HtmlHelper,
        防爆pression<&Func键LT;的TModel,TProperty>>前pression,
        IEnumerable的< TListItem> enumeratedItems,
        串idPropertyName,
        串displayPropertyName,
        串titlePropertyName,
        对象htmlAttributes)其中的TModel:类
    {
        //初始化值
        VAR元= ModelMetadata.FromLambdaEx pression(如pression,htmlHelper.ViewData);
        VAR propertyName的= metaData.PropertyName;
        VAR为PropertyValue = htmlHelper.ViewData.Eval(propertyName的).T​​oStringOrEmpty();
        VAR enumeratedType = typeof运算(TListItem);        //构建选择标签
        VAR returnText =的String.Format(<选择一个id = \\{0} \\NAME = \\{0} \\,HttpUtility.HtmlEn code(propertyName的));
        如果(htmlAttributes!= NULL)
        {
            的foreach(VAR KVP在htmlAttributes.GetType()的GetProperties()
             .ToDictionary(P => p.Name,P => p.GetValue(htmlAttributes,NULL)))
            {
                returnText + =的String.Format({0} = \\{1} \\,HttpUtility.HtmlEn code(kvp.Key)
                 HttpUtility.HtmlEn code(kvp.Value.ToStringOrEmpty()));
            }
        }
        returnText + => \\ N的;        //打造的选项标签
        的foreach(TListItem的listItem在enumeratedItems)
        {
            VAR idValue = enumeratedType.GetProperties()
             .FirstOrDefault(P => p.Name == idPropertyName)
             .GetValue(的listItem,NULL).ToStringOrEmpty();
            VAR titleValue = enumeratedType.GetProperties()
             .FirstOrDefault(P => p.Name == titlePropertyName)
             .GetValue(的listItem,NULL).ToStringOrEmpty();
            VAR displayValue = enumeratedType.GetProperties()
             .FirstOrDefault(P => p.Name == displayPropertyName)
             .GetValue(的listItem,NULL).ToStringOrEmpty();
            returnText + =的String.Format(<期权价值= \\{0} \\TITLE = \\{1} \\,
             HttpUtility.HtmlEn code(idValue),HttpUtility.HtmlEn code(titleValue));
            如果(idValue ==为PropertyValue)
            {
                returnText + =选择= \\选择\\;
            }
            returnText + =的String.Format(> {0}< /选项> \\ N,displayValue);
        }        //关闭选择标签
        returnText + =< /选择&g​​t;中;
        返回新HtmlString(returnText);
    }

...这个工程顺顺当当,但有些时候我想走得更远。我想自定义此兽的ID,显示和衔枚而无需编写出来的HTML。举例来说,如果我有一些类,像这样一个模型:

 公共类项目
{
    公众诠释的itemId {搞定;组; }
    公共字符串ITEMNAME {搞定;组; }
    公共字符串itemDescription {搞定;组; }
}公共类模型
{
    公共IEnumerable的<项目>项目{搞定;组; }
    公众诠释的itemId {搞定;组; }
}

在我看来,我可以这样写:

  @ Html.SelectFor(M = GT; m.itemId,Model.items的itemId,ITEMNAME,itemDescription,NULL)

...我会得到与标题属性等一个不错的下拉这是伟大的,只要列举的项目有属性正是我想显示它们。但我真的很想做的是一样的东西:

  @ Html.SelectFor(M = GT; m.itemId,Model.items,ID => id.itemId,DISP => disp.itemName,标题=>标题.itemName ++ title.itemDescription,NULL)

...并在这种情况下,上的选项title属性是 ITEMNAME 属性的串联和 itemDescription 属性。我承认拉姆达前pressions和LINQ功能的元级已经让我有点头晕。有人能指出我朝着正确的方向吗?

最终的结果对于那些谁是好奇,下面code让我在选择列表中的ID,使用lambda前pressions标题,并DisplayText性质完全控制:

 公共静态HtmlString SelectFor<的TModel,TProperty,TListItem>(
        这与的HtmlHelper LT;的TModel>的HtmlHelper,
        防爆pression<&Func键LT;的TModel,TProperty>>外汇pression,
        IEnumerable的< TListItem> enumeratedItems,
        属性与LT; TListItem> IDEX pression,
        属性与LT; TListItem> displayEx pression,
        属性与LT; TListItem> titleEx pression,
        对象htmlAttributes,
        布尔blankFirstLine)其中的TModel:类
    {
        //初始化值
        VAR元= ModelMetadata.FromLambdaEx pression(外汇pression,htmlHelper.ViewData);
        VAR propertyName的= metaData.PropertyName;
        VAR为PropertyValue = htmlHelper.ViewData.Eval(propertyName的).T​​oStringOrEmpty();
        VAR enumeratedType = typeof运算(TListItem);        //构建选择标签
        VAR returnText =的String.Format(<选择一个id = \\{0} \\NAME = \\{0} \\,HttpUtility.HtmlEn code(propertyName的));
        如果(htmlAttributes!= NULL)
        {
            的foreach(VAR KVP在htmlAttributes.GetType()的GetProperties()
             .ToDictionary(P => p.Name,P => p.GetValue(htmlAttributes,NULL)))
            {
                returnText + =的String.Format({0} = \\{1} \\,HttpUtility.HtmlEn code(kvp.Key)
                 HttpUtility.HtmlEn code(kvp.Value.ToStringOrEmpty()));
            }
        }
        returnText + => \\ N的;        如果(blankFirstLine)
        {
            returnText + =<期权价值= \\\\>< /选项>中;
        }        //打造的选项标签
        的foreach(TListItem的listItem在enumeratedItems)
        {
            VAR idValue = IDEX pression(的listItem).ToStringOrEmpty();
            VAR displayValue = displayEx pression(的listItem).ToStringOrEmpty();
            VAR titleValue = titleEx pression(的listItem).ToStringOrEmpty();
            returnText + =的String.Format(<期权价值= \\{0} \\TITLE = \\{1} \\,
                HttpUtility.HtmlEn code(idValue),HttpUtility.HtmlEn code(titleValue));
            如果(idValue ==为PropertyValue)
            {
                returnText + =选择= \\选择\\;
            }
            returnText + =的String.Format(> {0}< /选项> \\ N,displayValue);
        }        //关闭选择标签
        returnText + =< /选择&g​​t;中;
        返回新HtmlString(returnText);
    }    公共委托对象属性< T>(T的listItem);


解决方案

如果你不需要单独选项的title属性的code可以简化为:

 公共静态HtmlString SelectFor<的TModel,TProperty,TIdProperty,TDisplayProperty,TListItem>(
    这与的HtmlHelper LT;的TModel>的HtmlHelper,
    防爆pression<&Func键LT;的TModel,TProperty>>前pression,
    IEnumerable的< TListItem> enumeratedItems,
    防爆pression<&Func键LT; TListItem,TIdProperty>> idProperty,
    防爆pression<&Func键LT; TListItem,TDisplayProperty>> displayProperty,
    对象htmlAttributes
)其中的TModel:类
{
    VAR ID =(idProperty.Body作为MemberEx pression).Member.Name;
    VAR显示=(displayProperty.Body作为MemberEx pression).Member.Name;
    VAR选择列表=新的SelectList(enumeratedItems,ID,显示器);
    VAR属性=新RouteValueDictionary(htmlAttributes);
    返回htmlHelper.DropDownListFor(如pression,选择列表,属性);
}

和使用这样的:

  @ Html.SelectFor(
    M => m.itemId,
    Model.items,
    ID => id.itemId,
    DISP => disp.itemName,
    空值


如果你需要在标题属性,那么,你将必须实现的一切,将DropDownList帮手手工做这可能是相当的痛苦。这里的所有功能,只有一小部分的例子:

 公共静态类HtmlExtensions
{
    私有类MySelectListItem:SelectListItem
    {
        公共字符串名称{搞定;组; }
    }    公共静态HtmlString SelectFor<的TModel,TProperty,TIdProperty,TDisplayProperty,TListItem>(
        这与的HtmlHelper LT;的TModel>的HtmlHelper,
        防爆pression<&Func键LT;的TModel,TProperty>>前pression,
        IEnumerable的< TListItem> enumeratedItems,
        防爆pression<&Func键LT; TListItem,TIdProperty>> idProperty,
        防爆pression<&Func键LT; TListItem,TDisplayProperty>> displayProperty,
        FUNC< TListItem,串> titleProperty,
        对象htmlAttributes
    )其中的TModel:类
    {
        变量名称=前pressionHelper.GetEx pressionText(如pression);
        VAR fullHtmlName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(名);        VAR选择=新TagBuilder(选择);
        变种compiledDisplayProperty = displayProperty.Compile();
        变种compiledIdProperty = idProperty.Compile();
        select.GenerateId(fullHtmlName);
        select.MergeAttributes(新RouteValueDictionary(htmlAttributes));
        select.Attributes [名称] = fullHtmlName;
        VAR了selectedValue = htmlHelper.ViewData.Eval(fullHtmlName);
        VAR的选择=
            从我在enumeratedItems
            选择ListItemToOption(
                ItemToSelectItem(I,selectedValue,则compiledIdProperty,compiledDisplayProperty,titleProperty)
            );
        select.InnerHtml =的string.join(Environment.NewLine,期权);
        返回新HtmlString(select.ToString(TagRenderMode.Normal));
    }    私有静态MySelectListItem ItemToSelectItem< TListItem,TIdProperty,TDisplayProperty>(TListItem我,对象了selectedValue,Func键和LT; TListItem,TIdProperty> idProperty,Func键< TListItem,TDisplayProperty> displayProperty,Func键< TListItem,串> titleProperty)
    {
        VAR值= Convert.ToString(idProperty(I));
        返回新MySelectListItem
        {
            值=价值,
            文字= Convert.ToString(displayProperty(I)),
            标题= titleProperty(ⅰ),
            选择= Convert.ToString(了selectedValue)==值
        };
    }    私人静态字符串ListItemToOption(MySelectListItem项)
    {
        VAR建设者=新TagBuilder(选项);
        builder.Attributes [值] = item.Value;
        builder.Attributes [标题] = item.Title;
        builder.SetInnerText(item.Text);
        如果(item.Selected)
        {
            builder.Attributes [选择] =选择;
        }
        返回builder.ToString();
    }
}

和再使用这样的:

  @ Html.SelectFor(
    M => m.itemId,
    Model.items,
    ID => id.itemId,
    DISP => disp.itemName,
    标题=> title.itemName ++ title.itemDescription,
    空值

I wrote an HtmlHelper expression I use a lot of the time to put title tags into my dropdown lists like so:

    public static HtmlString SelectFor<TModel, TProperty, TListItem>(
        this HtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TProperty>> expression,
        IEnumerable<TListItem> enumeratedItems,
        string idPropertyName,
        string displayPropertyName,
        string titlePropertyName,
        object htmlAttributes) where TModel : class
    {
        //initialize values
        var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
        var propertyName = metaData.PropertyName;
        var propertyValue = htmlHelper.ViewData.Eval(propertyName).ToStringOrEmpty();
        var enumeratedType = typeof(TListItem);

        //build the select tag
        var returnText = string.Format("<select id=\"{0}\" name=\"{0}\"", HttpUtility.HtmlEncode(propertyName));
        if (htmlAttributes != null)
        {
            foreach (var kvp in htmlAttributes.GetType().GetProperties()
             .ToDictionary(p => p.Name, p => p.GetValue(htmlAttributes, null)))
            {
                returnText += string.Format(" {0}=\"{1}\"", HttpUtility.HtmlEncode(kvp.Key),
                 HttpUtility.HtmlEncode(kvp.Value.ToStringOrEmpty()));
            }
        }
        returnText += ">\n";

        //build the options tags
        foreach (TListItem listItem in enumeratedItems)
        {
            var idValue = enumeratedType.GetProperties()
             .FirstOrDefault(p => p.Name == idPropertyName)
             .GetValue(listItem, null).ToStringOrEmpty();
            var titleValue = enumeratedType.GetProperties()
             .FirstOrDefault(p => p.Name == titlePropertyName)
             .GetValue(listItem, null).ToStringOrEmpty();
            var displayValue = enumeratedType.GetProperties()
             .FirstOrDefault(p => p.Name == displayPropertyName)
             .GetValue(listItem, null).ToStringOrEmpty();
            returnText += string.Format("<option value=\"{0}\" title=\"{1}\"",
             HttpUtility.HtmlEncode(idValue), HttpUtility.HtmlEncode(titleValue));
            if (idValue == propertyValue)
            {
                returnText += " selected=\"selected\"";
            }
            returnText += string.Format(">{0}</option>\n", displayValue);
        }

        //close the select tag
        returnText += "</select>";
        return new HtmlString(returnText);
    }

...this works swimmingly, but there are times when I want to go further. I'd like to customize the id, display, and title pieces of this beast without having to write out the html. For example, if I have some classes in a model like so:

public class item
{
    public int itemId { get; set; }
    public string itemName { get; set; }
    public string itemDescription { get; set; }
}

public class model
{
    public IEnumerable<item> items { get; set; }
    public int itemId { get; set; }
}

In my view I can write:

@Html.SelectFor(m => m.itemId, Model.items, "itemId", "itemName", "itemDescription", null)

...and I'll get a nice dropdown with title attributes etc. This is great as long as the enumerated items have properties exactly as I'd like to display them. But what I'd really like to do is something like:

@Html.SelectFor(m => m.itemId, Model.items, id=>id.itemId, disp=>disp.itemName, title=>title.itemName + " " + title.itemDescription, null)

...and have, in this case, the title attribute on the options be a concatenation of the itemName property and the itemDescription property. I confess the meta-level of lambda expressions and Linq functions has got me a little dizzy. Can someone point me in the right direction?

FINAL RESULT For those who are curious, the following code gives me complete control over the select list's ID, Title, and DisplayText properties using lambda expressions:

    public static HtmlString SelectFor<TModel, TProperty, TListItem>(
        this HtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TProperty>> forExpression,
        IEnumerable<TListItem> enumeratedItems,
        Attribute<TListItem> idExpression,
        Attribute<TListItem> displayExpression,
        Attribute<TListItem> titleExpression,
        object htmlAttributes,
        bool blankFirstLine) where TModel : class
    {
        //initialize values
        var metaData = ModelMetadata.FromLambdaExpression(forExpression, htmlHelper.ViewData);
        var propertyName = metaData.PropertyName;
        var propertyValue = htmlHelper.ViewData.Eval(propertyName).ToStringOrEmpty();
        var enumeratedType = typeof(TListItem);

        //build the select tag
        var returnText = string.Format("<select id=\"{0}\" name=\"{0}\"", HttpUtility.HtmlEncode(propertyName));
        if (htmlAttributes != null)
        {
            foreach (var kvp in htmlAttributes.GetType().GetProperties()
             .ToDictionary(p => p.Name, p => p.GetValue(htmlAttributes, null)))
            {
                returnText += string.Format(" {0}=\"{1}\"", HttpUtility.HtmlEncode(kvp.Key),
                 HttpUtility.HtmlEncode(kvp.Value.ToStringOrEmpty()));
            }
        }
        returnText += ">\n";

        if (blankFirstLine)
        {
            returnText += "<option value=\"\"></option>";
        }

        //build the options tags
        foreach (TListItem listItem in enumeratedItems)
        {
            var idValue = idExpression(listItem).ToStringOrEmpty();
            var displayValue = displayExpression(listItem).ToStringOrEmpty();
            var titleValue = titleExpression(listItem).ToStringOrEmpty();
            returnText += string.Format("<option value=\"{0}\" title=\"{1}\"",
                HttpUtility.HtmlEncode(idValue), HttpUtility.HtmlEncode(titleValue));
            if (idValue == propertyValue)
            {
                returnText += " selected=\"selected\"";
            }
            returnText += string.Format(">{0}</option>\n", displayValue);
        }

        //close the select tag
        returnText += "</select>";
        return new HtmlString(returnText);
    }

    public delegate object Attribute<T>(T listItem);

解决方案

If you don't need the title attribute on individual options your code could be simplified to:

public static HtmlString SelectFor<TModel, TProperty, TIdProperty, TDisplayProperty, TListItem>(
    this HtmlHelper<TModel> htmlHelper,
    Expression<Func<TModel, TProperty>> expression,
    IEnumerable<TListItem> enumeratedItems,
    Expression<Func<TListItem, TIdProperty>> idProperty,
    Expression<Func<TListItem, TDisplayProperty>> displayProperty,
    object htmlAttributes
) where TModel : class
{
    var id = (idProperty.Body as MemberExpression).Member.Name;
    var display = (displayProperty.Body as MemberExpression).Member.Name;
    var selectList = new SelectList(enumeratedItems, id, display);
    var attributes = new RouteValueDictionary(htmlAttributes);
    return htmlHelper.DropDownListFor(expression, selectList, attributes);
}

and used like this:

@Html.SelectFor(
    m => m.itemId, 
    Model.items, 
    id => id.itemId, 
    disp => disp.itemName, 
    null
)


And if you need the title attribute, well, you will have to implement everything that the DropDownList helper does manually which could be quite of a pain. Here's an example of only a small portion of all the functionality:

public static class HtmlExtensions
{
    private class MySelectListItem : SelectListItem
    {
        public string Title { get; set; }
    }

    public static HtmlString SelectFor<TModel, TProperty, TIdProperty, TDisplayProperty, TListItem>(
        this HtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TProperty>> expression,
        IEnumerable<TListItem> enumeratedItems,
        Expression<Func<TListItem, TIdProperty>> idProperty,
        Expression<Func<TListItem, TDisplayProperty>> displayProperty,
        Func<TListItem, string> titleProperty,
        object htmlAttributes
    ) where TModel : class
    {
        var name = ExpressionHelper.GetExpressionText(expression);
        var fullHtmlName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);

        var select = new TagBuilder("select");
        var compiledDisplayProperty = displayProperty.Compile();
        var compiledIdProperty = idProperty.Compile();
        select.GenerateId(fullHtmlName);
        select.MergeAttributes(new RouteValueDictionary(htmlAttributes));
        select.Attributes["name"] = fullHtmlName;
        var selectedValue = htmlHelper.ViewData.Eval(fullHtmlName);
        var options = 
            from i in enumeratedItems
            select ListItemToOption(
                ItemToSelectItem(i, selectedValue, compiledIdProperty, compiledDisplayProperty, titleProperty)
            );
        select.InnerHtml = string.Join(Environment.NewLine, options);
        return new HtmlString(select.ToString(TagRenderMode.Normal));
    }

    private static MySelectListItem ItemToSelectItem<TListItem, TIdProperty, TDisplayProperty>(TListItem i, object selectedValue, Func<TListItem, TIdProperty> idProperty, Func<TListItem, TDisplayProperty> displayProperty, Func<TListItem, string> titleProperty)
    {
        var value = Convert.ToString(idProperty(i));
        return new MySelectListItem
        {
            Value = value,
            Text = Convert.ToString(displayProperty(i)),
            Title = titleProperty(i),
            Selected = Convert.ToString(selectedValue) == value
        };
    }

    private static string ListItemToOption(MySelectListItem item)
    {
        var builder = new TagBuilder("option");
        builder.Attributes["value"] = item.Value;
        builder.Attributes["title"] = item.Title;
        builder.SetInnerText(item.Text);
        if (item.Selected)
        {
            builder.Attributes["selected"] = "selected";
        }
        return builder.ToString();
    }
}

and then use like this:

@Html.SelectFor(
    m => m.itemId, 
    Model.items, 
    id => id.itemId, 
    disp => disp.itemName, 
    title => title.itemName + " " + title.itemDescription, 
    null
)

这篇关于有趣(?)与扩展方法LINQ的防爆pressions的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆