Шаблон (?!A)B
всегда будет совпадать на B, потому что очевидно, что вы не смотрите на A и B.Чтобы проверить, что было до B, вы можете использовать (отрицательный) прогноз:
^[a-z]{2,99}(?<!View)Model$
Так почему же не сработал прогноз?
Сопоставление происходит следующим образом (без возврата)
MyView | ^[a-z]{2,99} // Match as much as we can
| (?!View) // No "View" ahead, so we're good
Model | Model // normal match