Search code examples
c#linqasp.net-core-mvc

How can I express this nested loop in LINQ?


Background:

  • I have ASP.NET Core app. I'm trying to write server-side validation errors to the log.
  • If ASP.NET Core model validation detects any error in any field in the webform, then ModelState.IsValid is false.
  • Every field in the webform is listed in ModelState.Values
  • If a "Value" has one or more errors, then Value.Errors.Count > 0
  • I want to log a) the webform field name (the Value's "key") and b) each error message for that field.
  • I'd like to include all this information in a single line (i.e. a single C# "string").

Current code:

// LOG:
//   2022-10-24 09:37:29.0909|Error|ServiceMenu.Q255: The value '' is invalid. 
//   2022-10-24 09:37:35.4096|Error|ServiceMenu.Q265: The value '' is invalid. 
if (!ModelState.IsValid)
{
    foreach (var k in ModelState.Keys)
    {
        var v = ModelState[k];
        if (v.Errors.Count > 0)
        {
            string s = k + ": ";
            for (int i=0; i < v.Errors.Count - 1; i++)
                s += v.Errors[i].ErrorMessage + ", ";
            s += v.Errors[v.Errors.Count - 1].ErrorMessage;
            _logger.LogError(s);
        }
    }
    return Page();
}

Sample output (two required fields, Q255 and 265, were blank):

2022-10-24 09:37:29.0909|Error|ServiceMenu.Q255: The value '' is invalid. 
2022-10-24 09:37:35.4096|Error|ServiceMenu.Q265: The value '' is invalid. 

Can I simplify this nested loop with LINQ?


Solution

  • if (!ModelState.IsValid)
    {
        var logMessage = string.Join("\n", ModelState.Keys
            .Where(x => ModelState[x].Errors?.Count > 0)
            .Select(x => $"{x}: " + string.Join(", ", ModelState[x].Errors.Select(y => y.ErrorMessage))));
            
        _logger.LogError(logMessage);
    }