You could hardcode the categories "digit", "lowerCase" and "upperCase" and keep track of how many of each you've already added to the generated string:
public static string Generate(int size, int? maxDigits = null, int? maxLowerCase = null, int? maxUpperCase= null)
{
    if (maxDigits.HasValue && maxLowerCase.HasValue && maxUpperCase.HasValue && maxDigits + maxLowerCase + maxUpperCase< size)
    {
        throw new ArgumentOutOfRangeException($"Can't generate a string of length {size} with the given limits");
    }
    const string chars = "abcdefghijklmnopqrstuvwxzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    var passwordBuilder = new StringBuilder();
    var random = new Random();
    int digitCount = 0, lowerCaseCount = 0, upperCaseCount = 0;
    while (passwordBuilder.Length < size)
    {
        var nextCharacter = chars[random.Next(chars.Length)];
        if (char.IsDigit(nextCharacter) && (!maxDigits.HasValue || digitCount < maxDigits))
        {
            passwordBuilder.Append(nextCharacter);
            digitCount++;
        }
        if (char.IsLower(nextCharacter) && (!maxLowerCase.HasValue || lowerCaseCount < maxLowerCase))
        {
            passwordBuilder.Append(nextCharacter);
            lowerCaseCount++;
        }
        if (char.IsUpper(nextCharacter) && (!maxUpperCase.HasValue || upperCaseCount < maxUpperCase))
        {
            passwordBuilder.Append(nextCharacter);
            upperCaseCount++;
        }
    }
    return passwordBuilder.ToString();
}
Use like:
var password = Generate(10, maxDigits: 2);