Q:

Why is the modelstate invalid in my razor page when I use a class type as a parameter?

This is my first go at using Razor Pages in ASP.Net Core 2.2 and I'm utilizing a page filter to check the ModelState.IsValid which is returning false on GET requests when I use a type in an OnGet() method parameter which is causing the page to fail. If I change the signature of the method to use a primitive type, the model state is valid.
An example is  as follows:
ModelState.IsValid is false for the below code, which works fine in a controller action in ASP.Net Core 2.2 MVC:
public class IndexModel : PageModel
{
    public string IsValid { get; set; }

    public void OnGet(Query query)
    {
        // FALSE
        IsValid = ModelState.IsValid.ToString();
    }

    public class Query
    {
        public int? Page { get; set; }
    }
}

But it is true for the below:
public class IndexModel : PageModel
{
    public string IsValid { get; set; }

    public void OnGet(int? query)
    {
        // TRUE
        IsValid = ModelState.IsValid.ToString();
    }
}

Why is this happening?

A:

Instead of applying the Query type as a parameter to the OnGet method, you can add it as a PageModel property instead, decorated with the BindProperty attribute:
public class IndexModel : PageModel
{
    public string IsValid { get; set; }

    [BindProperty(SupportsGet=true)]
    public Query query { get; set; }

    public void OnGet()
    {

    }

    public class Query
    {
        public int? Page { get; set; }
    }
} 

When the BindProperty attribute is used, you have to opt in to binding taking place in GET request (hence SupportsGet=true), but when you work with PageModel properties, you don't need to tell model binding where to get the values from. It will automatically search query strings, the request body, route data etc.
More info: https://www.learnrazorpages.com/razor-pages/model-binding#binding-data-from-get-requests

A:

Razor Pages, by default, bind properties only with non-GET verbs.

Reference Introduction to Razor Pages in ASP.NET Core
Because by default, the GET is unable to bind to complex objects. Model binding attributes would need to be applied.
You have to explicitly tell it where you want to bind from. 
For example
public void OnGet([FromQuery]Query query) {
   IsValid = ModelState.IsValid.ToString();
}

