使用 OData 连接服务在 Blazor 客户端应用程序中使用 OData

Consuming OData in Blazor Client App using OData Connected Services

  1. 创建了 netstandard2.1 blazor web assembly 项目。
  2. 已将 Odata 连接服务 (V.0.10.0) 添加到同一项目。
  3. 生成了 OData 代理 class。
  4. 从 Razor 页面的 Task OnInitializedAsync() 方法调用了 odata 服务

从 Razor 页面调用 OData 服务:

protected override async Task OnInitializedAsync()
{
    await base.OnInitializedAsync();
    var context = new Container(new Uri("http://localhost/odata"));
    var result = await context.Students.ExecuteAsync();
}

浏览器异常:

blazor.webassembly.js:1 crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: Operation is not supported on this platform.
System.PlatformNotSupportedException: Operation is not supported on this platform.
  at System.Net.WebProxy.CreateDefaultProxy () <0x35560a0 + 0x00008> in <filename unknown>:0 
  at System.Net.Configuration.DefaultProxySectionInternal.GetSystemWebProxy () <0x3555f80 + 0x00000> in <filename unknown>:0 
  at System.Net.Configuration.DefaultProxySectionInternal.GetDefaultProxy_UsingOldMonoCode () <0x3555ed8 + 0x00000> in <filename unknown>:0 
  at System.Net.Configuration.DefaultProxySectionInternal.GetSection () <0x3555cd8 + 0x0002a> in <filename unknown>:0 
  at System.Net.WebRequest.get_InternalDefaultWebProxy () <0x3555a20 + 0x00034> in <filename unknown>:0 
  at System.Net.HttpWebRequest..ctor (System.Uri uri) <0x3555460 + 0x000b6> in <filename unknown>:0 
  at System.Net.HttpRequestCreator.Create (System.Uri uri) <0x3554aa0 + 0x00004> in <filename unknown>:0 
  at System.Net.WebRequest.Create (System.Uri requestUri, System.Boolean useUriBase) <0x354cfd8 + 0x00116> in <filename unknown>:0 
  at System.Net.WebRequest.Create (System.Uri requestUri) <0x354cc00 + 0x00024> in <filename unknown>:0 
  at Microsoft.OData.Client.HttpWebRequestMessage.CreateRequest (System.String method, System.Uri requestUrl, Microsoft.OData.Client.DataServiceClientRequestMessageArgs args) <0x353fd18 + 0x00006> in <filename unknown>:0 
  at Microsoft.OData.Client.HttpWebRequestMessage..ctor (Microsoft.OData.Client.DataServiceClientRequestMessageArgs args) <0x353f750 + 0x0006e> in <filename unknown>:0 
  at Microsoft.OData.Client.RequestInfo.CreateRequestMessage (Microsoft.OData.Client.BuildingRequestEventArgs requestMessageArgs) <0x353a6d0 + 0x0016a> in <filename unknown>:0 
  at Microsoft.OData.Client.ODataRequestMessageWrapper.CreateRequestMessageWrapper (Microsoft.OData.Client.BuildingRequestEventArgs requestMessageArgs, Microsoft.OData.Client.RequestInfo requestInfo) <0x353a0b0 + 0x0000e> in <filename unknown>:0 
  at Microsoft.OData.Client.ODataMessageWritingHelper.CreateRequestMessage (Microsoft.OData.Client.BuildingRequestEventArgs requestMessageArgs) <0x3539d68 + 0x0000a> in <filename unknown>:0 
  at Microsoft.OData.Client.DataServiceRequest.CreateExecuteResult (System.Object source, Microsoft.OData.Client.DataServiceContext context, System.AsyncCallback callback, System.Object state, System.String method) <0x3511280 + 0x00192> in <filename unknown>:0 
  at Microsoft.OData.Client.DataServiceRequest.BeginExecute (System.Object source, Microsoft.OData.Client.DataServiceContext context, System.AsyncCallback callback, System.Object state, System.String method) <0x3507818 + 0x00018> in <filename unknown>:0 
  at Microsoft.OData.Client.DataServiceQuery`1[TElement].BeginExecute (System.AsyncCallback callback, System.Object state) <0x3507610 + 0x0005a> in <filename unknown>:0 
  at System.Threading.Tasks.TaskFactory`1[TResult].FromAsyncImpl (System.Func`3[T1,T2,TResult] beginMethod, System.Func`2[T,TResult] endFunction, System.Action`1[T] endAction, System.Object state, System.Threading.Tasks.TaskCreationOptions creationOptions) <0x3506d10 + 0x00188> in <filename unknown>:0 
  at System.Threading.Tasks.TaskFactory`1[TResult].FromAsync (System.Func`3[T1,T2,TResult] beginMethod, System.Func`2[T,TResult] endMethod, System.Object state) <0x34fd5f8 + 0x00014> in <filename unknown>:0 
  at Microsoft.OData.Client.DataServiceQuery`1[TElement].ExecuteAsync () <0x34fce38 + 0x00028> in <filename unknown>:0 
  at BlazorClientApp2.Pages.Index.OnInitializedAsync () [0x00094] in ..\Pages\Index.razor:12 
  at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync () <0x2ec1cd8 + 0x0013a> in <filename unknown>:0

如何克服这个问题? 使用Odata Connected Service的主要原因是使用了它的属性追踪功能

Microsoft.OData.Client 7.8.1:

Working sample

//Program.cs Main
var httpClient = new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)};

builder.Services.AddScoped(sp => httpClient);

在 ODataServiceContext 中将 HttpRequestTransportMode 设置为 HttpRequestTransportMode.HttpClient:

  public class ODataServiceContext : DataServiceContext
  {
    public ODataServiceContext(Uri serviceRoot, HttpClient httpClient)
      : base(serviceRoot, ODataProtocolVersion.V4)
    {
      if(httpClient != null)
        HttpRequestTransportMode = HttpRequestTransportMode.HttpClient; 
      
      if (EdmModel == null)
      {
        switch (HttpRequestTransportMode)
        {
          case HttpRequestTransportMode.HttpClient: 
            Format.LoadServiceModel = () => GetServiceModelAsync(httpClient).Result;
            break;
          case HttpRequestTransportMode.HttpWebRequest: 
            Format.LoadServiceModel = () => GetServiceModel(GetMetadataUri());
            break;
        }
        
        Format.UseJson();
      }
      else
      {
        Format.UseJson(EdmModel);
      }
    }
    
    public static IEdmModel EdmModel { get; set; }

    //WASM support
    public static async Task<IEdmModel> GetServiceModelAsync(HttpClient httpClient)
    {
      using (var stream = await httpClient.GetStreamAsync("$metadata"))
      using (var reader = XmlReader.Create(stream))
      {
        return EdmModel = CsdlReader.Parse(reader);
      }
    }

    public IEdmModel GetServiceModel(Uri metadataUri)
    {
      var request = WebRequest.CreateHttp(metadataUri);

      using (var response = request.GetResponse())
      using (var stream = response.GetResponseStream())
      using (var reader = XmlReader.Create(stream))
      {
        return CsdlReader.Parse(reader);
      }
    }

    private DataServiceQuery<Student> student;

    public DataServiceQuery<Student> Students => student = student ?? CreateQuery<Student>("Students");
  }
//Blazor Wasm Component
using System.Net.HttpClient;

[Inject]
public HttpClient HttpClient { get; set; }

[Inject]
public NavigationManager NavigationManager { get; set; }

    protected override async Task OnInitializedAsync()
    {
      await base.OnInitializedAsync();

      Uri serviceRoot = NavigationManager.ToAbsoluteUri("/odata"); //new Uri("http://localhost/odata")

      var dataServiceContext =
        new ODataServiceContext(serviceRoot, HttpClient);

      var result = await dataServiceContext.Students.ExecuteAsync();
    }

Microsoft.OData.Client 7.6.4 (< 7.7.x):

使用这个包 Microsoft.OData.Extensions.Client 你将能够拦截 dataServiceContext.Configurations.RequestPipeline.OnMessageCreating :

//Program.cs Main
var httpClient = new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)};

builder.Services.AddTransient(sp => httpClient);

builder.Services.AddODataClient().AddHttpClient(httpClient);

//Blazor Wasm Component

using Microsoft.OData.Extensions.Client;

[Inject]
public IODataClientFactory ClientFactory { get; set; }
    
[Inject]
public NavigationManager NavigationManager { get; set; }

    protected override async Task OnInitializedAsync()
    {
      await base.OnInitializedAsync();

      Uri serviceRoot = NavigationManager.ToAbsoluteUri("/odata"); //new Uri("http://localhost/odata")

      var dataServiceContext =
        ClientFactory.CreateClient<ODataServiceContext>(serviceRoot);

      var result = await dataServiceContext.Students.ExecuteAsync();
    }