web-dev-qa-db-ja.com

ODataおよびWebAPI:モデルにナビゲーションプロパティがありません

Entity Framework、WebAPI、OData、およびAngularクライアントを使用して、簡単なおもちゃプロジェクトを組み立てようとしています。私のいずれかに配置したナビゲーションプロパティを除いて、すべてが正常に機能していますモデルが機能していないようです。$ expandを使用してAPIを呼び出すと、返されたエンティティにナビゲーションプロパティがありません。

私のクラスはDogとOwnerで、次のようになります。

    public class Dog
{
    // Properties
    [Key]
    public Guid Id { get; set; }
    public String Name { get; set; }
    [Required]
    public DogBreed Breed { get; set; }
    public int Age { get; set; }
    public int Weight { get; set; }


    // Foreign Keys
    [ForeignKey("Owner")]
    public Guid OwnerId { get; set; }

    // Navigation
    public virtual Owner Owner { get; set; }
}

    public class Owner
{
    // Properties
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }
    public string Phone { get; set; }
    public DateTime SignupDate { get; set; }

    // Navigation
    public virtual ICollection<Dog> Dogs { get; set; } 
}

また、クエリを処理するようにDogコントローラーをセットアップしました。

public class DogsController : ODataController
{
    DogHotelAPIContext db = new DogHotelAPIContext();
    #region Public methods 

    [Queryable(AllowedQueryOptions = System.Web.Http.OData.Query.AllowedQueryOptions.All)]
    public IQueryable<Dog> Get()
    {
        var result =  db.Dogs.AsQueryable();
        return result;
    }

    [Queryable(AllowedQueryOptions = System.Web.Http.OData.Query.AllowedQueryOptions.All)]
    public SingleResult<Dog> Get([FromODataUri] Guid key)
    {
        IQueryable<Dog> result = db.Dogs.Where(d => d.Id == key).AsQueryable().Include("Owner");
        return SingleResult.Create(result);
    }

    protected override void Dispose(bool disposing)
    {
        db.Dispose();
        base.Dispose(disposing);
    }

}

データベースにサンプルデータを少しシードしました。すべての犬のレコードには、Ownersテーブル内の所有者のIDと一致するOwnerIdがあります。

これを使用した犬のリストのクエリは正常に機能します。

http://localhost:49382/odata/Dogs

Ownerナビゲーションプロパティなしで、Dogエンティティのリストを取得します。

OData $ expandを使用して飼い主と一緒に犬をクエリすることはできません:

http://localhost:49382/odata/Dogs?$expand=Owner

私の応答は200で、すべてのDogエンティティが含まれていますが、JSONでそれらにOwnerプロパティがありません。

メタデータをクエリすると、ODataはそれについて知っているように見えます。

<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
  <edmx:DataServices>
    <Schema Namespace="DogHotelAPI.Models" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EntityType Name="Dog">
        <Key>
          <PropertyRef Name="id" />
        </Key>
        <Property Name="id" Type="Edm.Guid" Nullable="false" />
        <Property Name="name" Type="Edm.String" />
        <Property Name="breed" Type="DogHotelAPI.Models.Enums.DogBreed" Nullable="false" />
        <Property Name="age" Type="Edm.Int32" Nullable="false" />
        <Property Name="weight" Type="Edm.Int32" Nullable="false" />
        <Property Name="ownerId" Type="Edm.Guid" />
        <NavigationProperty Name="owner" Type="DogHotelAPI.Models.Owner">
          <ReferentialConstraint Property="ownerId" ReferencedProperty="id" />
        </NavigationProperty>
      </EntityType>
      <EntityType Name="Owner">
        <Key>
          <PropertyRef Name="id" />
        </Key>
        <Property Name="id" Type="Edm.Guid" Nullable="false" />
        <Property Name="name" Type="Edm.String" />
        <Property Name="address" Type="Edm.String" />
        <Property Name="phone" Type="Edm.String" />
        <Property Name="signupDate" Type="Edm.DateTimeOffset" Nullable="false" />
        <NavigationProperty Name="dogs" Type="Collection(DogHotelAPI.Models.Dog)" />
      </EntityType>
    </Schema>
    <Schema Namespace="DogHotelAPI.Models.Enums" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EnumType Name="DogBreed">
        <Member Name="AfghanHound" Value="0" />
        <Member Name="AmericanStaffordshireTerrier" Value="1" />
        <Member Name="Boxer" Value="2" />
        <Member Name="Chihuahua" Value="3" />
        <Member Name="Dachsund" Value="4" />
        <Member Name="GermanShepherd" Value="5" />
        <Member Name="GoldenRetriever" Value="6" />
        <Member Name="Greyhound" Value="7" />
        <Member Name="ItalianGreyhound" Value="8" />
        <Member Name="Labrador" Value="9" />
        <Member Name="Pomeranian" Value="10" />
        <Member Name="Poodle" Value="11" />
        <Member Name="ToyPoodle" Value="12" />
        <Member Name="ShihTzu" Value="13" />
        <Member Name="YorkshireTerrier" Value="14" />
      </EnumType>
    </Schema>
    <Schema Namespace="Default" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EntityContainer Name="Container">
        <EntitySet Name="Dogs" EntityType="DogHotelAPI.Models.Dog">
          <NavigationPropertyBinding Path="owner" Target="Owners" />
        </EntitySet>
        <EntitySet Name="Owners" EntityType="DogHotelAPI.Models.Owner">
          <NavigationPropertyBinding Path="dogs" Target="Dogs" />
        </EntitySet>
      </EntityContainer>
    </Schema>
  </edmx:DataServices>
</edmx:Edmx>

私のナビゲーションのpreoprtyがモデルの残りの部分に戻るのを妨げている何が欠けているのでしょうか?

[〜#〜]編集[〜#〜]

問題をさらに分離するために、サーバー側のC#に所有者を含めてみました。 DogコントローラーのGetメソッドに次の行を追加しました。

var test = db.Dogs.Include("Owner").ToList();

これで、関連する所有者が含まれていることをデバッグして確認できます。各犬には、このリストで関連付けられている所有者がいます。

実際に返されるものに対して.Include( "Owner")を使用しても問題は解決されません-プロパティはまだクライアントに到達しません。

これは、ナビゲーションプロパティは機能しているが、クライアントに送り返されていないことを意味しているようです。これは、傷がODataまたはWebAPIの問題を示しているように思われますが、私は推測しますが、何なのかわかりません。

また、循環ナビゲーションプロパティを処理するために、Global.asaxファイルのApplication_Startに次の行を追加しました。

            var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
        json.SerializerSettings.PreserveReferencesHandling =
            Newtonsoft.Json.PreserveReferencesHandling.All;

循環参照がなんらかの原因である場合に備えてそれを行いましたが、これは何も変わりません。

[〜#〜]更新[〜#〜]

に電話をかけていることに気づきました

http://localhost:49382/odata/Dogs(abfd26a5-14d8-4b14-adbe-0a0c0ef392a7)/owner

動作します。これにより、その犬に関連付けられている所有者が取得されます。これはさらに、私のナビゲーションプロパティが正しく設定されていることを示しています。これらは、$ expandを使用した呼び出しへの応答に含まれていません

更新2

これが私のWebApiConfigファイルのregisterメソッドです:

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

        ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
        builder.EnableLowerCamelCase();
        builder.EntitySet<Dog>("Dogs");
        builder.EntitySet<Owner>("Owners");

        config.EnableQuerySupport();

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


        // Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
        // To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
        // For more information, visit http://go.Microsoft.com/fwlink/?LinkId=279712.
        //config.EnableQuerySupport();

        // To disable tracing in your application, please comment out or remove the following line of code
        // For more information, refer to: http://www.asp.net/web-api
        config.EnableSystemDiagnosticsTracing();
    }
14
MWinstead

私は自分の問題の解決策を見つけました。これは最終的に3つの原因によって引き起こされました。

1.)廃止予定のコントローラーメソッドで[Queryable]属性を使用していました。新しい[EnableQuery]属性を使用する必要がありました。

2.)私のWebApiConfig.csファイルで、デフォルトのconfig.EnableQuerySupport()を使用してクエリを有効にしていました。これは非推奨で、削除になっています。

3.)必要な拡張呼び出しは$ expand = Ownerの形式でしたが、$ expand = ownerの形式にする必要がありました。これは、ODataConventionModelBuilderでキャメルの小文字を有効にするためです。 Mark Bennetts氏の回答にこれを指摘していただき、ありがとうございました。

これらの変更をすべて行った後、関連するOwnerエンティティがDogエンティティとともに返されます。

10
MWinstead

これはあなたが使っているからです

builder.EnableLowerCamelCase();

oDataConventionModelBuilderセットアップで。

クエリオプションの$ expand句で "所有者"を認識しません。大文字と小文字が区別されるため、そのパスはODataモデルに実際には存在しないためです。

この/ Dogs?$ expand = ownerをリクエストしようとすると、これでうまくいき、Dogとその所有者の両方がJSON応答で返されます。

6
Mark Bennetts

私は非常に類似した問題を抱えていましたが、これはまったく同じ問題が原因であると考えています。

エンティティのグラフ全体を返すバインドされたOData関数を作成しようとしました。これにより、すべてに対して$ expand句を指定する必要がなく、特定の状況でクライアントの作業が少し楽になります。

エンティティフレームワークのlinq呼び出しでIncludeステートメントを指定し、デバッグで返されるデータが実際に完全に入力されていることを確認しましたが、あなたのように、何もない状態で最上位のエンティティが返されるだけでした。

問題は、ODataに使用されるシリアル化にあります

あなたが見つけるものは、Ownerクラスから主キーを削除して基本的に複雑なエンティティになる場合、ODataシリアル化されたJSON結果に含まれ、そうでなければODataリクエストURIが$ expandを構成しない限り含まれません。それを含む節。

$ expand句をコードに挿入して機能させる方法を見つけようとしましたが、残念ながら空白になりました。

お役に立てれば

1
Mark Bennetts

以下がうまくいくかどうかを確認してください。私はOData v4でテストしているので、_[EnableQuery]_を_[Queryable]_に調整する必要があるかもしれません。 dbコンテキストは、.AsQueryable()が不要になるようなIQueryable結果を返す必要があります。

_// GET: odata/Dogs
[EnableQuery]
public IQueryable<Dog> Get()
{
    return db.Dogs;
}

// GET: odata/Dogs(5)/Owner
[EnableQuery]
public IQueryable<Owner> GetOwner([FromODataUri] int key)
{
    return db.Dogs.Where(m => m.ID == key).SelectMany(m => m.Owner);
}
_

私はあなたが私が現在取り組んでいる小さなプロジェクトとあなたが持っているものを比較しています。これはおそらくそうではありませんが、私のFKアソシエーションの設定は少し異なり、たぶん、FKの順序が問題なのかもしれません。私の外部キーはnavプロパティの上に飾られているようです。

_public int PublicImageID { get; set; }
[ForeignKey("PublicImageID")]
public PublicImage PublicImage { get; set; }

// Foreign Keys    
public Guid OwnerId { get; set; }
[ForeignKey("OwnerId")]
public virtual Owner Owner { get; set; }
_
0
Pynt