最近我们决定在我们的服务中使用包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行
答案 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();
}
}
}