在.Net Core

时间:2018-01-29 10:31:03

标签: c# asp.net-core odata

最近我们决定在我们的服务中使用包Microsoft.AspNetCore.OData作为我们的解决方案。这些服务具有ODataQueryOptions参数,并使用它来过滤它们提供的数据。 要进行单元测试,我需要以某种方式模拟ODataQueryOptions。 以前使用System.Web.Http.OData.Query.ODataQueryOptions更容易,因为您可以使用HttpRequestMessage作为参数创建它,但不能再创建它。

我有这段代码

 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);
    }

此代码抛出下一个异常:

  

System.ArgumentNullException:值不能为null。参数名称:provider      在Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService [T](IServiceProvider提供程序)      在Microsoft.AspNet.OData.Extensions.HttpRequestExtensions.CreateRequestScope(HttpRequest请求,String routeName)      在Microsoft.AspNet.OData.Extensions.HttpRequestExtensions.CreateRequestContainer(HttpRequest请求,String routeName)      在Microsoft.AspNet.OData.Extensions.HttpRequestExtensions.GetRequestContainer(HttpRequest请求)      在Microsoft.AspNet.OData.Query.ODataQueryOptions..ctor(ODataQueryContext上下文,HttpRequest请求)      在Microsoft.AspNet.OData.Query.ODataQueryOptions 1..ctor(ODataQueryContext context, HttpRequest request) at Services.Test.Internal.ODataFactory.Create[T](String url, Action 1 reconfigure)C:\ Users \ wboun \ source \ repos \ Services.Test \ Internal \ ODataFactory.cs:第36行

3 个答案:

答案 0 :(得分:3)

我想出了如何超越Value cannot be null. Parameter name: provider错误和Cannot find the services container for the non-OData route.

首先,当您创建DefaultHttpContext对象时,必须将RequestServices设置为collection.BuildServiceProvider()。

接下来,您必须启用EnableDependencyInjection。我能够通过使用Moq来做到这一点。

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

我的完整代码是:

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);

答案 1 :(得分:0)

通过ODataQueryOptions的代码后,我们找到了解决方案。

主要问题是缺少服务提供程序中所需对象的初始化,并将其传递给ODataConventionModelBuilder构造函数

ServiceProvider GetServiceProvider()
    {
        var collection = new ServiceCollection();

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

        return collection.BuildServiceProvider();
    }

然后可以使用

模拟HttpRequest
 var uri = new Uri(url);

        HttpRequest request = new DefaultHttpRequest(http) {
            Method = "GET",
            Host = new HostString(uri.Host, uri.Port),
            Path = uri.LocalPath,
            QueryString = new QueryString(uri.Query)
        };

答案 2 :(得分:0)

根据@mccow002 的回答,我只是发布了 ODataHelper 和所需的命名空间(因为这也给我带来了麻烦),我在需要 ODataQueryOptions 使其工作的单元测试中使用它:

using Microsoft.AspNet.OData;
using Microsoft.AspNet.OData.Builder;
using Microsoft.AspNet.OData.Extensions;
using Microsoft.AspNet.OData.Query;
using Microsoft.AspNet.OData.Query.Validators;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OData.UriParser;
using Moq;
using System;

namespace Surgent.Evaluation.Tests.xUnit.Helpers
{
    public static class ODataHelper
    {
        public static ODataQueryOptions<T> Create<T>(string uri = "")
                where T : class
        {
            var provider = GetServiceProvider();
            var routeBuilder = new RouteBuilder(Mock.Of<IApplicationBuilder>(x => x.ApplicationServices == provider));
            routeBuilder.EnableDependencyInjection();            

            var modelBuilder = new ODataConventionModelBuilder(provider);
            modelBuilder.EntitySet<T>(nameof(T));
            var model = modelBuilder.GetEdmModel();

            var http = new DefaultHttpContext();
            var uri = new Uri(uri);

            http.Request.Method = "GET";
            http.Request.Host = new HostString(uri.Host, uri.Port);
            http.Request.Path = uri.LocalPath;
            http.Request.QueryString = new QueryString(uri.Query);
            http.RequestServices = provider;
                        
            HttpRequest request = http.Request;
            var context = new ODataQueryContext(model, typeof(T), new Microsoft.AspNet.OData.Routing.ODataPath());

            return new ODataQueryOptions<T>(context, request);
        }

        private static ServiceProvider GetServiceProvider()
        {
            var collection = new ServiceCollection();

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

            return collection.BuildServiceProvider();
        }
    }
}