5

WebApi で odata を使用することを検討しています。これまでのところ非常に優れており、wcf データ サービスよりも柔軟な点が気に入っています。

ただし、モデルで仮想 IQueryable プロパティを使用しようとすると、問題が発生します。

たとえば、次のモデル クラスがあります。

public class MainItem
{
    public int Id { get; set;}
    public virtual IEnumerable<SubItem> SubItems { get; set;}
}

そして、私の MainItemsController は次のようになります

public class MainItemsController : EntitySetController<MainItem, int>
{
    [Queryable]
    public override IQueryable<MainItem> Get()
    {
        return SomeMainItemIQueryable();
    }

    public override GetEntityByKey(int key) 
    {
        return SingleMainItem(key);
    }

    [Queryable]
    public IQueryable GetSubItems(int key)
    {
        return SomeSubItemIQueryable(SingleMainItem(key));
    }
}

次の URL で正しい結果が得られます: /odata/MainItems /odata/MainItems(1) /odata/MainItems(1)/SubItems

しかし、/odata/MainItems(1)/SubItems(1) を実行しようとすると

このエラーが表示されます このサービスは、フォーム '~/entityset/key/navigation/key' の OData 要求をサポートしていません

この呼び出しと /odata/MainItems(1)/SubItems を SubItemsController にリダイレクトしたいと思います。

カスタム ODataPathHandler を作成することでおそらくこれを行うことができますが、それはこれを行う正しい方法とは思えません。

4

1 に答える 1

5

それは正しい。そのためのカスタム パス ハンドラは必要ありません。これは、理解できる有効な OData URL を表し、それを に解析できますODataPath。必要なのは、カスタム ルーティング規則です。ルーティング規則は、ODataPath をコントローラーとアクションにマップします。デフォルトでは、WCF DS クライアントが生成する URL を処理する基本的なルーティング規則のみが出荷されます。ルーティング規則がない URL を使用しているようです。とはいえ、書くのは簡単です。例、

public class ContainmentRoutingConvention : IODataRoutingConvention
{
    public string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup<string, HttpActionDescriptor> actionMap)
    {
        IEdmEntitySet entitySet = odataPath.EntitySet;

        if (odataPath.PathTemplate == "~/entityset/key/navigation")
        {
            controllerContext.RouteData.Values["key"] = (odataPath.Segments[1] as KeyValuePathSegment).Value;
            return "Get" + entitySet.Name;
        }
        else if (odataPath.PathTemplate == "~/entityset/key/navigation/key")
        {
            controllerContext.RouteData.Values["key1"] = (odataPath.Segments[1] as KeyValuePathSegment).Value;
            controllerContext.RouteData.Values["key2"] = (odataPath.Segments[3] as KeyValuePathSegment).Value;
            return "Get" + entitySet.ElementType.Name;
        }

        return null;
    }

    public string SelectController(ODataPath odataPath, HttpRequestMessage request)
    {
        if (odataPath.PathTemplate == "~/entityset/key/navigation" ||
            odataPath.PathTemplate == "~/entityset/key/navigation/key")
        {
            IEdmNavigationProperty navigationProperty = (odataPath.Segments[2] as NavigationPathSegment).NavigationProperty;
            IEdmEntitySet entitySet = odataPath.EntitySet; // the target entity set, which would be 'SubItems';

            return entitySet.Name;
        }

        return null;
    }
}

これは、あなたが言及した2つのURLのみを処理します. 他の URL もサポートするように拡張するのは簡単です。

~/entityset/key/navigation を処理するアクションの署名は でありIEnumerable<Order> GetOrders(int key)、URL ~/entityset/key/navigation/key の場合はOrder GetOrder(int key1, int key2).

于 2013-03-29T18:04:36.693 に答える