[英]ODATA: unbound function returning a complex type with a collection
我一直在嘗試使用 odata 和 ASP.NET Core 3 來實現一些東西,但它不想正常工作,而且我似乎無法弄清楚出了什么問題。 我創建了一個小示例應用程序來演示。
我有一個可用於查詢節點的 odata 服務。 節點可以是type1或type2節點,這些是開放類型,具有動態屬性。 查詢它們非常有效。 我想要做的是計算節點之間的路徑。 路徑不是實體——它們沒有身份。 所以我認為為此創建資源是不正確的。 它們只是路徑計算的結果,包含沿路徑的節點列表,所以我認為 function 是告訴 API 我想要什么的更好方法。
所以我創建了一個 odata function 來進行計算並返回可用路徑,它工作正常,除了我無法讓它返回路徑遍歷的節點列表,這是我真正需要的信息。
我創建了一些示例代碼來演示該問題:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.OData;
using Microsoft.AspNet.OData.Builder;
using Microsoft.AspNet.OData.Extensions;
using Microsoft.AspNet.OData.Routing;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace OdataSample
{
public static class Program {
public static void Main(string[] args) {
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
public class Startup {
public void ConfigureServices(IServiceCollection services) {
services.AddOData();
services.AddSingleton<IDataProvider, DataProvider>();
services.AddMvc(options => options.EnableEndpointRouting = false);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
app.UseDeveloperExceptionPage();
var builder = new ODataConventionModelBuilder(app.ApplicationServices);
builder.EntitySet<Node>("Nodes");
builder.ComplexType<Path>()
.HasMany(x => x.Nodes)
.HasDerivedTypeConstraints(typeof(Type1Node), typeof(Type2Node));
var calculatePath = builder.Function("CalculatePaths");
calculatePath.Parameter<string>("source");
calculatePath.Parameter<string>("target");
calculatePath.ReturnsCollection<Path>();
app.UseMvc(routeBuilder =>
{
routeBuilder.EnableDependencyInjection();
routeBuilder.Select().Expand().Filter().OrderBy().MaxTop(10).Count();
routeBuilder.MapODataServiceRoute("ODataRoute", "odata", builder.GetEdmModel());
});
}
}
public abstract class Node {
public string Id { get; set; }
public string Kind { get; set; }
public IDictionary<string, object> CustomProperties { get; set; }
}
public sealed class Type1Node : Node {
}
public sealed class Type2Node : Node {
public string Source { get; set; }
public string Target { get; set; }
}
public sealed class Path {
public string SourceId { get; set; }
public string TargetId { get; set; }
public List<Node> Nodes { get; set; }
}
public interface IDataProvider {
Task<IEnumerable<Node>> GetNodes();
Task<IEnumerable<Path>> GetPaths(string source, string target);
}
public sealed class DataProvider : IDataProvider {
private static readonly IList<Node> Nodes = new List<Node> {
new Type1Node{Id = "first", Kind="type1-kind1", CustomProperties = new Dictionary<string, object>()},
new Type1Node{Id = "second", Kind = "type1-kind2", CustomProperties = new Dictionary<string, object>{{"foo", "bar"}}},
new Type2Node{Id = "third", Kind="type2-kind1", Source = "first", Target = "second"},
new Type2Node{Id = "fourth", Kind="type2-kind1", Source = "first", Target = "second", CustomProperties = new Dictionary<string, object>{{"red", "blue"}}}
};
public async Task<IEnumerable<Node>> GetNodes() {
await Task.Yield();
return Nodes.ToList();
}
public async Task<IEnumerable<Path>> GetPaths(string source, string target) {
await Task.Yield();
return new List<Path> {
new Path { SourceId = source, TargetId = target, Nodes = new List<Node> {Nodes[0], Nodes[2], Nodes[1]}},
new Path { SourceId = source, TargetId = target, Nodes = new List<Node> {Nodes[0], Nodes[3], Nodes[1]}}};
}
}
public class NodesController : ODataController {
private readonly IDataProvider dataProvider;
public NodesController(IDataProvider dataProvider) => this.dataProvider = dataProvider;
[EnableQuery]
public async Task<List<Node>> Get() => (await dataProvider.GetNodes()).ToList();
}
public class PathsController : ODataController {
private readonly IDataProvider dataProvider;
public PathsController(IDataProvider dataProvider) => this.dataProvider = dataProvider;
[EnableQuery]
[HttpGet]
[ODataRoute("CalculatePaths")]
public async Task<List<Path>> Get(string source, string target) =>
(await dataProvider.GetPaths(source, target)).ToList();
}
}
很抱歉丑陋,我盡量壓縮它。
現在http://host:port/odata/CalculatePaths?source=A&target=B
應該返回 2 條路徑,確實如此。 但是只有兩個字符串屬性在那里,集合屬性不是:
GET
host:port/odata/CalculatePaths?source=A&target=B
將返回: {"@odata.context":"http://host:port/odata/$metadata#Collection(OdataSample.Path)","value":[{"SourceId":"A","TargetId":"B"},{"SourceId":"A","TargetId":"B"}]}
我試着用很多不同的方式來擺弄它,但並不開心。 我唯一一次接近我想要的是當我將路徑更改為只有節點 ID(字符串)而不是節點時。 但這並不理想,因為我需要查詢各個節點,即使我已經擁有所需的所有信息。
我應該更改什么以便在響應中也顯示節點?
我嘗試了您的代碼並得到了相同的結果,但未能在結果中包含Nodes
。 我對EntitySet<Path>
進行了一些更改以很好地獲取Nodes
,但不知道它是否適合您。
builder.EntitySet<Path>("CalculatePaths");
//builder.ComplexType<Path>()
// .HasMany(x => x.Nodes)
// .HasDerivedTypeConstraints(typeof(Type1Node), typeof(Type2Node));
需要將密鑰Id
添加到Path
實體。
public sealed class Path
{
public int Id { get; set; }
public string SourceId { get; set; }
public string TargetId { get; set; }
public List<Node> Nodes { get; set; }
}
你期望的結果
相關鏈接: 復雜類型的導航屬性
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.