C# 在.Net内核上模拟ODataQueryOptions

C# 在.Net内核上模拟ODataQueryOptions,c#,asp.net-core,odata,C#,Asp.net Core,Odata,最近,我们决定在我们的服务中使用解决方案包。这些服务具有ODataQueryOptions参数,并使用它来过滤它们提供的数据。 为了进行单元测试,我需要以某种方式模拟ODataQueryOptions。 以前使用System.Web.Http.OData.Query.ODataQueryOptions更容易,因为您可以使用HttpRequestMessage作为参数来创建它,但现在不能了 我有这个密码 public static ODataQueryOptions<T> Creat

最近,我们决定在我们的服务中使用解决方案包。这些服务具有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。参数名称:提供程序 位于Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T]IServiceProvider提供程序 在Microsoft.AspNet.OData.Extensions.HttpRequestExtensions.CreateRequestScopeHttpRequest请求中,字符串routeName 在Microsoft.AspNet.OData.Extensions.HttpRequestExtensions.CreateRequestContainerHttpRequest请求中,字符串routeName 位于Microsoft.AspNet.OData.Extensions.HttpRequestExtensions.GetRequestContainerHttpRequest请求 在Microsoft.AspNet.OData.Query.ODataQueryOptions..ctorODataQueryContext上下文中,HttpRequest请求 在Microsoft.AspNet.OData.Query.odataQueryOptions 1..ctorODataQueryContext上下文中,HttpRequest请求 在Services.Test.Internal.ODataFactory.Create[T]字符串url中,操作1在C:\Users\wboun\source\repos\Services.Test\Internal\ODataFactory.cs中重新配置:第36行


在仔细研究了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();
    }

在仔细研究了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();
    }

我知道了如何通过值不能为null。参数名称:提供程序错误,无法找到非OData路由的服务容器

首先,创建DefaultHttpContext对象时,必须将RequestServices设置为collection.BuildServiceProvider

接下来,您必须启用DependencyInjection。我可以通过使用最小起订量来做到这一点

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

我知道了如何通过值不能为null。参数名称:提供程序错误,无法找到非OData路由的服务容器

首先,创建DefaultHttpContext对象时,必须将RequestServices设置为collection.BuildServiceProvider

接下来,您必须启用DependencyInjection。我可以通过使用最小起订量来做到这一点

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

根据@mccow002-answer,我只是发布了带有所需名称空间的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();
        }
    }
}

根据@mccow002-answer,我只是发布了带有所需名称空间的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();
        }
    }
}

我遇到了完全相同的问题,搜索得越多,找到的就越少。最后,我找到了这篇文章,并尝试了你的建议,正如你在下面的要点中所看到的,但仍然不起作用,并给了我同样的错误信息。有什么想法吗?如果你能给我完整的异常堆栈就好了。还有环境。因为这发生在我身上是因为AspNetCore 2Tanks@Mathean。我通过使用AddMvc而不是AddMvcCore解决了这个问题。现在还有另一个问题,关于路由,它要求我启用依赖注入。但在单元测试中,我找不到任何.NETCore的解决方案。任何暗示都将不胜感激。异常->是的,我已经搜索了一些东西,在iaapplicationbuilder.UseMvc中,您必须将路由配置放在如下位置:routes.Filter.EnableDependencyInjection;通过将DefaultHttpContext中的RequestServices属性设置为services.BuildServiceprovider,我能够克服空提供程序问题,但现在我遇到了一个错误:无法找到非OData路由的服务容器。这通常通过调用routes.EnableDependencyInjection来解决,但我不知道如何在单元测试中设置路由。有什么想法吗?我遇到了完全相同的问题,我搜索得越多,找到的就越少。最后,我找到了这篇文章,并尝试了你的建议,正如你在下面的要点中所看到的,但仍然不起作用,并给了我同样的错误信息。有什么想法吗?如果你能给我完整的异常堆栈就好了。还有环境。因为这发生在我身上是因为AspNetCore 2Tanks@Mathean。我通过使用AddMvc而不是AddMvcCore解决了这个问题。现在还有另一个问题,关于路由,它要求我启用依赖注入。但在单元测试中,我找不到任何.NETCore的解决方案。任何h
非常感谢。异常->是的,我已经搜索了一些东西,在iaapplicationbuilder.UseMvc中,您必须将路由配置放在如下位置:routes.Filter.EnableDependencyInjection;通过将DefaultHttpContext中的RequestServices属性设置为services.BuildServiceprovider,我能够克服空提供程序问题,但现在我遇到了一个错误:无法找到非OData路由的服务容器。这通常通过调用routes.EnableDependencyInjection来解决,但我不知道如何在单元测试中设置路由。有什么想法吗?我确认这已经解决了问题,我的测试现在通过了以前没有的点。谢谢,我可以确认一下。这是我在.NET Core 3.1中使用XUnit和Moq的解决方案。我确认这已经解决了问题,我的测试现在通过了以前没有的点。谢谢,我可以确认一下。这是我在.NETCore3.1中使用XUnit和Moq的解决方案。