代码之家  ›  专栏  ›  技术社区  ›  Mathean

正在对ODataQueryOptions进行模拟。净核心

  •  2
  • Mathean  · 技术社区  · 7 年前

    最近,我们决定使用该软件包 Microsoft.AspNetCore.OData 为我们的服务提供解决方案。这些服务具有ODataQueryOptions参数,并使用它来过滤它们提供的数据。 为了进行单元测试,我需要以某种方式模拟ODataQueryOptions。 过去使用系统更容易。网状物Http。奥达塔。查询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);
        }
    

    此代码引发下一个异常:

    系统ArgumentNullException:值不能为null。参数名称:提供程序 在微软。扩展。依赖注入。ServiceProviderServiceExtensions。GetRequiredService[T](IServiceProvider提供程序) 在微软。AspNet。奥达塔。扩展。HttpRequestExtensions。CreateRequestScope(HttpRequest请求,字符串routeName) 在微软。AspNet。奥达塔。扩展。HttpRequestExtensions。CreateRequestContainer(HttpRequest请求,字符串routeName) 在微软。AspNet。奥达塔。扩展。HttpRequestExtensions。GetRequestContainer(HttpRequest请求) 在微软。AspNet。奥达塔。查询ODataQueryOptions。。ctor(ODataQueryContext上下文,HttpRequest请求) 在微软。AspNet。奥达塔。查询ODataQueryOptions 1..ctor(ODataQueryContext context, HttpRequest request) at Services.Test.Internal.ODataFactory.Create[T](String url, Action 1重新配置)在C:\Users\wboun\source\repos\Services中。测试\内部\ ODataFactory。cs:第36行

    3 回复  |  直到 7 年前
        1
  •  9
  •   mccow002    6 年前

    我知道如何通过 Value cannot be null. Parameter name: provider 错误和 Cannot find the services container for the non-OData route.

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

    接下来,必须启用DependencyInjection。我可以通过使用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);
    
        2
  •  1
  •   Mathean    7 年前

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

    然后可以用

     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)
            };
    
        3
  •  0
  •   Mauro Bilotti    4 年前

    根据@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();
            }
        }
    }