Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking ODataQueryOptions on .Net Core

Recently we decided to use the package Microsoft.AspNetCore.OData for our solution in our services. The services have the ODataQueryOptions parameter and use it for filtering the data they provide. To unit test this I need to Mock ODataQueryOptions somehow. It used to be easier with System.Web.Http.OData.Query.ODataQueryOptions because you could create it with HttpRequestMessage as parameter but not any more.

I have this code

 public static ODataQueryOptions<T> Create<T>(string url = "", Action<ODataConventionModelBuilder> reconfigure = null)
               where T : class
    {
        var serviceCollection = new ServiceCollection();
        serviceCollection.AddMvcCore().AddApplicationPart(typeof(ODataFactory).Assembly);

        ODataConventionModelBuilder builder = new ODataConventionModelBuilder(serviceCollection.BuildServiceProvider());
        builder.EntitySet<T>("Entity");

        reconfigure?.Invoke(builder);

        ODataQueryContext context = new ODataQueryContext(builder.GetEdmModel(), typeof(T), new Microsoft.AspNet.OData.Routing.ODataPath());

        var httpContext = new DefaultHttpContext();

        var httpRequest = new DefaultHttpRequest(httpContext);

        // throws exception: Value cannot be null. Parameter name: provider
        return new ODataQueryOptions<T>(context, httpRequest);
    }

This code throws the next exception:

System.ArgumentNullException: Value cannot be null. Parameter name: provider at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider) at Microsoft.AspNet.OData.Extensions.HttpRequestExtensions.CreateRequestScope(HttpRequest request, String routeName) at Microsoft.AspNet.OData.Extensions.HttpRequestExtensions.CreateRequestContainer(HttpRequest request, String routeName) at Microsoft.AspNet.OData.Extensions.HttpRequestExtensions.GetRequestContainer(HttpRequest request) at Microsoft.AspNet.OData.Query.ODataQueryOptions..ctor(ODataQueryContext context, HttpRequest request) at Microsoft.AspNet.OData.Query.ODataQueryOptions1..ctor(ODataQueryContext context, HttpRequest request) at Services.Test.Internal.ODataFactory.Create[T](String url, Action1 reconfigure) in C:\Users\wboun\source\repos\Services.Test\Internal\ODataFactory.cs:line 36

like image 250
Mathean Avatar asked Aug 30 '26 01:08

Mathean


1 Answers

I figured out how to get past the Value cannot be null. Parameter name: provider error and the Cannot find the services container for the non-OData route.

First off, when you create the DefaultHttpContext object, you have to set RequestServices to collection.BuildServiceProvider().

Next, you have to EnableDependencyInjection. I was able to do that by using Moq.

var routeBuilder = new RouteBuilder(Mock.Of<IApplicationBuilder>(x => x.ApplicationServices == provider));
routeBuilder.EnableDependencyInjection();

My full code is:

var collection = new ServiceCollection();

collection.AddOData();
collection.AddODataQueryFilter();
collection.AddTransient<ODataUriResolver>();
collection.AddTransient<ODataQueryValidator>();
collection.AddTransient<TopQueryValidator>();
collection.AddTransient<FilterQueryValidator>();
collection.AddTransient<SkipQueryValidator>();
collection.AddTransient<OrderByQueryValidator>();

var provider = collection.BuildServiceProvider();

var routeBuilder = new RouteBuilder(Mock.Of<IApplicationBuilder>(x => x.ApplicationServices == provider));
routeBuilder.EnableDependencyInjection();

var modelBuilder = new ODataConventionModelBuilder(provider);
modelBuilder.EntitySet<MyType>("MyType");
var model = modelBuilder.GetEdmModel();

var uri = new Uri("http://localhost/api/mytype/12345?$select=Id");

var httpContext = new DefaultHttpContext{
    RequestServices = provider
};
HttpRequest req = new DefaultHttpRequest(httpContext)
{
    Method = "GET",
    Host = new HostString(uri.Host, uri.Port),
    Path = uri.LocalPath,
    QueryString = new QueryString(uri.Query)
};

var context = new ODataQueryContext(model, typeof(MyType), new Microsoft.AspNet.OData.Routing.ODataPath());
var options = new ODataQueryOptions<MyType>(context, req);
like image 66
mccow002 Avatar answered Sep 01 '26 19:09

mccow002



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!