4

I have created my own Html Helper which adds red asterisks to any required field.

It successfully works with both

@Html.myLabelFor(model => model.Description)
//and
@Html.myLabelFor(model => model.Description, new { /*stuff*/ })

However, some of the code lines are like following

@Html.myLabelFor(model => model.Description, "Deletion Reason", new { /*stuff*/ })

My method was not designed to handle 3 parameters, so I added a caller which would handle 3 parameters

public static MvcHtmlString myLabelFor<TModel, TValue>(this HtmlHelper<TModel> html,
     Expression<Func<TModel, TValue>> expression, string labelText, Object htmlAttributes)
  {
      return myLabelFor(html, expression, labelText, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
  }    

Below are other methods that are working properly (including internal, which contains all necessary code and whose structure I used as a reference)

public static MvcHtmlString myLabelFor<TModel, TValue>(this HtmlHelper<TModel> html,
   Expression<Func<TModel, TValue>> expression, IDictionary<String, Object> htmlAttributes)
  {
      return LabelHelper(html, ModelMetadata.FromLambdaExpression(expression, html.ViewData),
          ExpressionHelper.GetExpressionText(expression), null, htmlAttributes);
  }

  public static MvcHtmlString myLabelFor<TModel, TValue>(this HtmlHelper<TModel> html,
  Expression<Func<TModel, TValue>> expression)
  {
      return LabelHelper(html, ModelMetadata.FromLambdaExpression(expression, html.ViewData),
          ExpressionHelper.GetExpressionText(expression), null);
  }

  public static MvcHtmlString myLabelFor<TModel, TValue>(this HtmlHelper<TModel> html,
      Expression<Func<TModel, TValue>> expression, Object htmlAttributes)
  {
      return myLabelFor(html, expression, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
  }

  //USED ITS STRUCTURE AS A REFERENCE
  internal static MvcHtmlString LabelHelper(HtmlHelper html, ModelMetadata metadata, String htmlFieldName,
      String labelText = null, IDictionary<String, Object> htmlAttributes = null)

Logically, I was expecting that parameter labelText would take a value of "Deletion Reason" from the line of code above. However, instead it had thrown a StackOverflowException inside my 3-parameter method. Microsoft description was vague, additional explanation did not help, and additional solution was using

Expression<Func<TModel, string>> expression instead of my Expression<Func<TModel, TValue>> expression

I do not understand what I am doing wrong. At this point I can only think of "fiddle with parameters until it works", but I am hopeful there is more elegant solution to that problem.

PS: Please let me know if my code for internal helper will help to solve the problem.

Community
  • 1
  • 1
Vadzim Savenok
  • 930
  • 3
  • 14
  • 37
  • Because your first `myLabelFor()` method is calling itself (not `LabelHelper()`) which in turn calls itself again, which in turn calls itself again and so on and so on. –  Jun 23 '16 at 22:53
  • Your first `myLabelFor` method calls itself! – DavidG Jun 23 '16 at 22:54
  • @DavidG You mean the one with string labelText? – Vadzim Savenok Jun 23 '16 at 22:55
  • Yes, that one, it recursively calls itself. – DavidG Jun 23 '16 at 22:56
  • @DavidG But what about the method inside big block with html, expression, and htmlAttributes, which also returns myLabelFor? It did not throw exception there, which is why I decided to use it instead of Label helper, since changing to LabelHelper causes errors. – Vadzim Savenok Jun 23 '16 at 22:59
  • The one inside the 'big block' (the 3rd one) calls the 1st overload which in turn calls `LabelHelper()` –  Jun 23 '16 at 23:06
  • @StephenMuecke Then, the reason why `return LabelHelper(html, expression, labelText, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));` returns error is because I am calling an overload method with wrong set of parameters? – Vadzim Savenok Jun 23 '16 at 23:10
  • The top one needs to be `return LabelHelper(html, ModelMetadata.FromLambdaExpression(expression, html.ViewData), ExpressionHelper.GetExpressionText(expression), labelText, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));` but you should remove `= null` from the last parameter in `LabelHelper` and explicitly pass `null` in the other overloads –  Jun 23 '16 at 23:18
  • @StephenMuecke Unfortunately, I have to leave now, so I will only be able to explicitly test your suggestion tomorrow. I will let you know about results, and, if successful, you can write it as an answer and I will accept it! Until tomorrow's testing everyone! – Vadzim Savenok Jun 23 '16 at 23:24
  • @StephenMuecke And thank you in advance for trying to help! – Vadzim Savenok Jun 23 '16 at 23:29
  • @DavidG Thank you for helping! – Vadzim Savenok Jun 23 '16 at 23:30
  • I'll add an answer later with other suggested improvements as well. –  Jun 23 '16 at 23:31

1 Answers1

2

You getting an exception on the first overload, because the method is recursively calling itself, and keeps doing so until the execution stack overflows. Rather than calling itself you need to change

return myLabelFor(html, 
                  expression,
                  labelText,
                  HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));

to

return LabelHelper(html,
                   ModelMetadata.FromLambdaExpression(expression, html.ViewData),
                   ExpressionHelper.GetExpressionText(expression),
                   labelText,
                   HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));

From your comments, the reason your 4th overload which uses return myLabelFor(...) does not throw the exception is because it calls your 2nd overload which in turn calls return LabelHelper(...)

I recommend that you change the 4th overload to call LabelHelper() directly, and change all the public overloads to explicitly call LabelHelper(), passing all 4 parameters, which is the pattern used by the in-built `HtmlHelper extension methods (you can view the source code for LabelFor() here)

  • Thank you very much Stephen! It works, and now I have learned the cause of it, along with some interesting source code. I am accepting your answer! – Vadzim Savenok Jun 24 '16 at 15:24