1

Web Api 2 OData v4 サービスで設定されたエンティティのプロパティとしてテーブル値関数を公開する方法を理解するのに助けが必要です。

私の単純化されたスキーマには、Structures、Locations、および LocationLinks の 3 つのテーブルがあります。構造体には、ノード (Locatons) とエッジ (LocationLinks) を持つグラフが含まれています。Entity Framework 6 データベースの最初のモデルでアクセスします。

Simplified Schema

  Structure:
      ID

  Locations:
      ID
      ParentID -> Structure

  LocationLinks
      A -> Location
      B -> Location

目標は、構造物の位置にアクセスするのと同じ方法で、LocationLinks の構造物コレクションにアクセスすることです。つまり、構造 #180 のグラフを要求するには:

http://.../OData/Structures(180)/LocationLinks
http://.../OData/Structures(180)/Locations

Locations クエリは自動的に機能しますが、正しいルートを追加して LocationLinks クエリを有効にする方法がわかりません。タスクが簡単になると考えて、テーブル値関数を SQL サーバーに追加しました。関数は私の EF モデルに存在し、LocationLink エンティティのコレクションを返します。

StructureLocationLinks(@StructureID) -> LocationLinks

残念ながら、私が何をしようとしても、Structure(180)\LocationLinks URL を機能させることができないようです。これは私の最新の試みです:

StructuresController.cs スニペット:

        // GET: odata/Structures(5)/Locations
        [EnableQuery]
        public IQueryable<Location> GetLocations([FromODataUri] long key)
        {
            return db.Structures.Where(m => m.ID == key).SelectMany(m => m.Locations);
        }

        // GET: odata/Structures(5)/LocationLinks
        [EnableQuery]        
        //[System.Web.OData.Routing.ODataRoute("Structures({key})")]
        public IQueryable<LocationLink> GetLocationLinks([FromODataUri] long key)
        {
            return db.StructureLocationLinks(key);
        }

WebApi.cs スニペット:

public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
            var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
            json.UseDataContractJsonSerializer = true;
            //json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.All;

            //var cors = new System.Web.Http.Cors.EnableCorsAttribute("*", "*", "*");
            //config.EnableCors(cors);

            // Web API routes 
            config.MapHttpAttributeRoutes();

            ODataConventionModelBuilder builder = GetModel();

            config.MapODataServiceRoute(routeName: "odata",
                routePrefix: null,
                model: builder.GetEdmModel());

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }

    public static ODataConventionModelBuilder GetModel()
        {
            ODataConventionModelBuilder builder = new ODataConventionModelBuilder();

            builder.Namespace = typeof(Structure).Namespace;

            AddLocationLinks(builder);
            AddStructures(builder);
            builder.EntitySet<Location>("Locations");

            return builder;
        }

        public static void AddStructures(ODataModelBuilder builder)
        {
            var structSetconfig = builder.EntitySet<Structure>("Structures");
            var structConfig = structSetconfig.EntityType;

            var functionConfig = structConfig.Collection.Function("StructureLocationLinks");
            functionConfig.Parameter<long>("StructureID");
            functionConfig.Returns<LocationLink>();
        }


        public static void AddLocationLinks(ODataModelBuilder builder)
        {
            var type = builder.EntityType<LocationLink>();
            type.HasKey(sl => sl.A);
            type.HasKey(sl => sl.B);
            builder.EntitySet<LocationLink>("LocationLinks");
        }

私が受け取るエラーは次のとおりです。

{ "エラー":{ "コード":"","メッセージ":"要求 URI ' http://.../OData/Structures(180)/LocationLinks ' に一致する HTTP リソースが見つかりませんでした。"," innererror":{ "message":"テンプレート '~/entityset/key/unresolved' を使用して OData パスのアクションを選択するためのルーティング規則が見つかりませんでした。","type":"","stacktrace":"" } } }

いくつかの検索に基づいて、ODataRoute 属性をコントローラーに追加しようとしました。

// GET: odata/Structures(5)/LocationLinks
[EnableQuery]        
[System.Web.OData.Routing.ODataRoute("Structures({key})/LocationLinks")]
public IQueryable<LocationLink> GetLocationLinks([FromODataUri] long key)
{
    return db.StructureLocationLinks(key);
}

次のエラーが発生します。

コントローラー 'Structures' のアクション 'GetLocationLinks' のパス テンプレート 'Structures({key})/LocationLinks' は、有効な OData パス テンプレートではありません。OData パス テンプレート 'Structures({key})/LocationLinks' に未解決のパス セグメント 'LocationLinks' が見つかりました。

Structures コレクションから LocationLinks を公開するにはどうすればよいですか? お時間をいただきありがとうございます。

編集:

この質問を見つけた後、私はなんとかこれを機能させることができました: カスタムクエリをサポートするナビゲーションプロパティを ODataConventionModelBuilder に追加する

ODataConventionBuilder オブジェクトで .GetEdmModel を呼び出し、次の関数を使用してナビゲーション プロパティをモデルに追加する必要がありました。

private static Microsoft.OData.Edm.IEdmModel AddStructureLocationLinks(IEdmModel edmModel)
        { 

            var structures = edmModel.EntityContainer.FindEntitySet("Structures") as EdmEntitySet;
            var locationLinks = edmModel.EntityContainer.FindEntitySet("LocationLinks") as EdmEntitySet;
            var structType = structures.EntityType() as EdmEntityType;
            var locLinksType = locationLinks.EntityType() as EdmEntityType;

            var structLocLinksProperty = new EdmNavigationPropertyInfo();
            structLocLinksProperty.TargetMultiplicity = Microsoft.OData.Edm.EdmMultiplicity.Many;
            structLocLinksProperty.Target = locLinksType;
            structLocLinksProperty.ContainsTarget = true; 
            structLocLinksProperty.OnDelete = Microsoft.OData.Edm.EdmOnDeleteAction.None;
            structLocLinksProperty.Name = "LocationLinks";

            var navigationProperty = structType.AddUnidirectionalNavigation(structLocLinksProperty);
            structures.AddNavigationTarget(navigationProperty, locationLinks);

            return edmModel; 
        }

私が今抱えている問題は、クエリでナビゲーション プロパティにアクセスする機能が制限されていることです。たとえば、このリンクは機能します:

http://.../OData/Structures(180)/Children?$expand=Locations

これはそうではありませんが。

http://.../OData/Structures(180)/Children?$expand=LocationLinks

返されるエラーは

{ "error": { "code":"","message":"エラーが発生しました。","innererror": { "message":"インスタンス プロパティ 'LocationLinks' は型 'ConnectomeDataModel.Structure' に対して定義されていません", "type":"System.ArgumentException","stacktrace":" System.Linq.Expressions.Expression.Property(Expression expression, String propertyName) で \r\n System.Web.OData.Query.Expressions.SelectExpandBinder で.CreatePropertyValueExpressionWithFilter(IEdmEntityType elementType, IEdmProperty property, Expression source, FilterClause filterClause)\r\n at System.Web.OData.Query.Expressions.SelectExpandBinder.BuildPropertyContainer(IEdmEntityType elementType, Expression source,ディクショナリ2 propertiesToExpand, ISet1 propertiesToInclude、ISet1 autoSelectedProperties, Boolean isSelectingOpenTypeSegments)\r\n at System.Web.OData.Query.Expressions.SelectExpandBinder.ProjectElement(Expression source, SelectExpandClause selectExpandClause, IEdmEntityType entityType)\r\n at System.Web.OData.Query.Expressions.SelectExpandBinder.Bind(IQueryable queryable)\r\n at System.Web.OData.Query.ODataQueryOptions.ApplySelectExpand[T](T entity, ODataQuerySettings querySettings)\r\n at System.Web.OData.Query.ODataQueryOptions.ApplyTo(IQueryable query, ODataQuerySettings querySettings)\r\n at System.Web.OData.EnableQueryAttribute.ExecuteQuery(Object response, HttpRequestMessage request, HttpActionDescriptor actionDescriptor)\r\n at System.Web.OData.EnableQueryAttribute.OnActionExecuted(HttpActionExecutedContext actionExecutedContext)\r\n at System.Web.Http.Filters.ActionFilterAttribute.OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__5.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter1.GetResult()\r\n System.Web.Http.Controllers.ActionFilterResult.d__2.MoveNext()\r\n--- 例外がスローされた前の場所からのスタック トレースの終わり ---\r\n System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (タスク タスク) で\r\n System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (タスク タスク) で\r\n System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() で\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()" } } }

4

1 に答える 1

1

ザイコール

あなたから言われたように、LocationLinksと同じでなければなりませLocationsStructure。したがって、コントローラーとアクションに対して行ったことは正しいと思います。あなたの説明に基づいてテストを行いましたが、Web API OData はGetLocationLinks規則に従ってルーティングできるようです。

私のメタデータ ドキュメントを見せてください。名前空間を無視して、あなたのものと比較し、違いを教えてください。ありがとう。

<Schema Namespace="ODataConsoleSample" xmlns="http://docs.oasis-open.org/odata/ns/edm">
  <EntityType Name="LocationLink">
    <Key>
      <PropertyRef Name="A" />
      <PropertyRef Name="B" />
    </Key>
    <Property Name="A" Type="Edm.Int64" Nullable="false" />
    <Property Name="B" Type="Edm.Int64" Nullable="false" />
  </EntityType>
  <EntityType Name="Structure">
    <Key>
      <PropertyRef Name="Id" />
    </Key>
    <Property Name="Id" Type="Edm.Int64" Nullable="false" />
    <NavigationProperty Name="Locations" Type="Collection(ODataConsoleSample.Location)" />
    <NavigationProperty Name="LocationLinks" Type="Collection(ODataConsoleSample.LocationLink)" />
  </EntityType>
  <EntityType Name="Location">
    <Key>
      <PropertyRef Name="Id" />
    </Key>
    <Property Name="Id" Type="Edm.Int64" Nullable="false" />
    <Property Name="ParentId" Type="Edm.Int64" Nullable="false" />
  </EntityType>
  <Function Name="StructureLocationLinks" IsBound="true">
    <Parameter Name="bindingParameter" Type="Collection(ODataConsoleSample.Structure)" />
    <Parameter Name="StructureID" Type="Edm.Int64" Nullable="false" />
    <ReturnType Type="ODataConsoleSample.LocationLink" />
  </Function>
  <EntityContainer Name="Container">
    <EntitySet Name="LocationLinks" EntityType="ODataConsoleSample.LocationLink" />
    <EntitySet Name="Structures" EntityType="ODataConsoleSample.Structure">
      <NavigationPropertyBinding Path="Locations" Target="Locations" />
      <NavigationPropertyBinding Path="LocationLinks" Target="LocationLinks" />
    </EntitySet>
    <EntitySet Name="Locations" EntityType="ODataConsoleSample.Location" />
  </EntityContainer>
</Schema>
于 2015-09-06T02:04:32.190 に答える