Convert Expression to Expression>

2019-04-12 15:56发布

问题:

Simple question really.

I have MVC view that displays a Nullable Bool, e,g,

Html.CheckBoxFor(model=>model.NullableBoolHere, Model.NullableBoolHere, 

and I want to create a new html helper, that will accept this type, and then convert

Null || False => False
True => True

so I have the following

public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool?>> expression, object htmlAttributes, bool disabled)
    {
        IDictionary<string, object> values = new RouteValueDictionary(htmlAttributes);

        if (disabled)
            values.Add("disabled", "true");

        Expression<Func<TModel, bool>> boolExpression = CONVERT_TO_BOOL_HERE(expression);


        return htmlHelper.CheckBoxFor(expression, values);
    }

Any help appreciated, I understand I will have to use recursion to copy the expression, but just not sure how to go about navigating the expression itself, find the bool?, convert to bool.

回答1:

You can use this code:

var body = Expression.Coalesce(expression.Body, Expression.Constant(false));
var boolExpression = (Expression<Func<TModel, bool>>)
    Expression.Lambda(body, expression.Parameters.First());

The advantage to the other answers is that it doesn't compile the first expression, it just wraps it. The resulting expression is similar to one created by this code:

m => m.NullableBoolHere ?? false

Check it live.



回答2:

So, in the end, the only way I could find to do it was to resolve the bool? into a bool myself, then return a 'normal' checkbox by passing in the correct name etc.

This does work a treat though, so all good. If you do know a better way of getting the correct ParameterName, it would be great to hear.

public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool?>> expression, object htmlAttributes, bool disabled)
    {
        IDictionary<string, object> values = new RouteValueDictionary(htmlAttributes);

        if (disabled)
            values.Add("disabled", "true");

        //Compile the expression to get the value from it.
        var compiled = expression.Compile().Invoke(htmlHelper.ViewData.Model);
        bool checkValue = compiled.HasValue ? compiled.Value : false; //evaluate the compiled expression

        //Get the name of the id we should use
        //var parameterName = ((MemberExpression)expression.Body).Member.Name; // only gives the last part
        string parameterName = expression.Body.ToString().Replace("model.", "");//.Replace(".", HtmlHelper.IdAttributeDotReplacement);

        //Return our 'hand made' checkbox
        return htmlHelper.CheckBox(parameterName, checkValue, values);
    }


回答3:

I guess it won't be enough to just convert the expression to another type, MVC uses expressions for a reason so I suspect it needs to examine the given expression and apply some magic on it.

You can create a new expression that does the conversion, like this:

 Expression<Func<TModel, bool>> boolExpression = 
        T => expression.Compile()(T).GetValueOrDefault(false);

But as I said, I suspect it will not be enough, MVC probably wants to examine model members etc inside the expression.



回答4:

How about this:

 Expression<Func<TModel, bool>> boolExpression = model =>
        {
             bool? result = expression.Compile()(model);

             return result.HasValue ? result.Value : false;
        };

That way you wrap the original expression and you can convert the result from bool? to bool.

Does it solve your problem?