10

Ich versuche, ein einfaches Spielzeugprojekt mit Entity Framework, WebAPI, OData und einem Angular-Client zusammenzustellen. Alles funktioniert gut, außer dass die Navigationseigenschaft, die ich auf eines meiner Modelle gesetzt habe, nicht funktioniert. Wenn ich meine API mit $ expand aufrufen, haben die zurückgegebenen Entitäten ihre Navigationseigenschaften nicht.OData und WebAPI: Navigationseigenschaft nicht vorhanden auf Modell

sind meine Klassen Hund und Besitzer, und sehen wie folgt aus:

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; } 
} 

Ich habe Abfrage mein Hund Controller einrichten zu handhaben auch:

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); 
    } 

} 

Ich habe die Datenbank mit einem ausgesät Bit der Beispieldaten. Alle Hundedatensätze haben eine Eigentümer-ID, die mit der ID eines Eigentümers in der Tabelle Besitzer übereinstimmt.

für die Liste der Hunde Abfragen dies mit adaequat:

http://localhost:49382/odata/Dogs 

ich eine Liste von Hunde-Einheiten erhalten, ohne dass die Eigentümer Navigationseigenschaft.

Abfragen für die Hunde mit ihren Besitzern OData $ mit expand nicht funktioniert:

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

Meine Antwort ist ein 200 mit all den Hundeeinheiten, aber keiner von ihnen eine Owner-Eigenschaft haben auf sie in der JSON.

Wenn ich meine Metadaten abfragen, finde ich, dass OData darüber zu wissen scheint:

<?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> 

Was könnte ich das fehlt, ist meine Navigation preoprty verhindert mit dem Rest meines Modells kommen zurück?

EDIT

weiter Um das Problem, das ich auf der Server-Seite einschließlich der Eigentümer in C# versucht zu isolieren. Ich habe diese Zeile in der Get-Methode meiner Hunde Controller:

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

Damit ich debuggen können und sehen, dass die damit verbundenen Besitzer enthalten sind. Jeder Hund hat den Besitzer, der ihm in dieser Liste zugeordnet ist.

Die Verwendung von .Include ("Besitzer") auf, was tatsächlich zurückgegeben wird, behebt das Problem nicht - die Eigenschaften erreichen immer noch nicht den Client.

Dies scheint zu bedeuten, dass die Navigationseigenschaften funktionieren, aber nicht zurück zum Client gesendet werden. Es scheint, als ob die Wunde auf ein Problem mit OData oder WebAPI hinweist, würde ich schätzen, aber ich bin mir nicht sicher was.

Außerdem habe ich die folgenden Zeilen Application_Start in meinem Global.asax-Datei, um zu handhaben Kreisnavigationseigenschaften hinzugefügt:

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

Ich habe das eine kreisförmige Referenz im Falle war irgendwie der Täter, aber diese ändert nichts.

UPDATE

Ich bemerkte, dass ein Aufruf an

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

Arbeiten zu machen. Dies ruft den Besitzer dieses Hundes ab. Dies zeigt weiter, dass meine Navigationseigenschaften korrekt eingerichtet sind, nur werden nicht in Antworten auf Aufrufe mit $ expand enthalten.

UPDATE 2

Hier ist das Register Methode meiner WebApiConfig Datei:

 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(); 
    } 
+0

In Tests auf meinem eigenen funktionalen OData v4-Setup. Ich habe festgestellt, dass durch das Einfügen des Includes ("RelatedEntity") auch die zugehörige Entität nicht zurückgegeben wurde. Ich versuchte zu untersuchen, wie der $ expand-Befehl debuggt wird, sobald er an die API übergeben wurde, konnte die Informationen jedoch nicht finden. Ich würde vorschlagen, wenn es möglich ist, das Gerüst zu verwenden, das mit Visual Studio zur Verfügung gestellt wird, um einen Besitzer- und Hundecontroller zu erzeugen. Passen Sie Ihre [Queryable] an Ihre Anforderungen an, indem Sie die erzeugten generischen Daten verwenden, und versuchen Sie es auf diese Weise, wenn dies überhaupt möglich ist. – Pynt

+0

Ich denke immer noch, dass es nur an die Navigationseigenschaft und Fremdschlüsseleinstellung gebunden sein kann. Versuchen Sie möglicherweise eine OData-Abfrage in einer separaten Klasse mit einem Unterbrechungspunkt, um festzustellen, ob Sie beide Datenpunkte abrufen können, um zu überprüfen, ob die Beziehung mithilfe einer generischen Entitätsabfrage eingerichtet wurde. – Pynt

Antwort

0

Prüfen Sie, ob die unten könnte für Sie arbeiten. Ich teste in OData v4, so dass Sie möglicherweise [EnableQuery] zu [Queryable] anpassen müssen. Ihr Db-Kontext sollte ein IQueryable-Ergebnis zurückgeben, so dass .AsQueryable() möglicherweise nicht benötigt wird.

// 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); 
} 

Ich vergleiche, was Sie zu einem kleinen Projekt haben, an dem ich gerade arbeite. Dies ist wahrscheinlich nicht der Fall, aber meine FK-Assoziation ist etwas anders aufgebaut und vielleicht ist die Ordnung des FK das Problem. Meine Fremdschlüssel scheinen auf den Navigationseigenschaften zu liegen.

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

Ich habe das versucht, und ich sehe immer noch das Gleiche. Beachten Sie, dass ich Queryable verwenden muss und Abfrageoptionen explizit darin zulassen darf, um etwas anderes als eine Fehlermeldung zu erhalten. – MWinstead

+0

Auch interessant für mich ist, dass, selbst wenn ich .Include ("Besitzer") in meinem Server-Side-Controller, die Owner-Eigenschaft nicht mit den Dog-Entitäten in meiner Antwort gesendet wird. – MWinstead

+0

Ich vergleiche einen Funktionscode, den ich habe, der sehr ähnlich ist. Ich habe ein Beispiel für meine Foreign-Key-Erklärung hinzugefügt, um zu sehen, ob durch irgendeinen Zufall, dass der Schuldige sein könnte. – Pynt

1

Ich hatte ein sehr ähnliches Problem, das, glaube ich, durch genau das gleiche Problem verursacht wird.

Ich habe versucht, einige gebundene OData-Funktionen zu erstellen, die ganze Graphen von Entitäten zurückgeben würden, um den Client-Job in bestimmten Situationen etwas einfacher zu machen, anstatt $ expand-Klauseln für alles anzugeben.

Ich habe Include-Anweisungen in den linq-Aufrufen des Entitätsframeworks angegeben und überprüft, dass die Rückgabedaten zwar vollständig in debug gefüllt waren, aber wie Sie habe ich immer nur die Entität der obersten Ebene mit nichts anderem zurückgegeben.

Das Problem liegt bei der Serialisierung für OData verwendet

Was Sie finden, dass, wenn Sie den Primärschlüssel aus Ihrem Besitzer Klasse entfernen, so dass es im Wesentlichen ein komplexes Gebilde wird, dann wird es in der OData aufgenommen werden serialisiertes JSON-Ergebnis, andernfalls wird es nicht verwendet, es sei denn, die OData-Anfrage-URL enthält eine $ expand-Klausel, die sie enthält.

Ich habe versucht, einen Weg zu finden, $ expand-Klauseln in den Code einzufügen, damit es funktioniert, aber leider leer ausging.

Hoffnung hilft diese

+0

Danke für den Kommentar. Ich bin mir nicht sicher, wie genau es sich auf mein Problem bezieht: Ich habe bereits den Schlüssel [Schlüssel] in der Owner-Klasse nicht angegeben und verwende eine $ expand-Klausel, um sie mit meinen Dog-Entitäten zusammenzuführen. Was genau schlägst du vor? – MWinstead

+0

Verdächtiger Sie werden finden, dass [Schlüssel] abgeleitet wird, weil Sie eine Id-Eigenschaft haben - wetten, wenn Sie diese Eigenschaft entfernen, wird es funktionieren. –

+0

Ich kann dies nicht überprüfen, denn wenn ich das tue, dann config.MapODataServiceRoute Fehler, die angeben, dass ein Schlüssel fehlt. Und selbst wenn dies nicht der Fall sein sollte, während dies das zugrundeliegende Problem beleuchten könnte, wäre es noch weit davon entfernt, eine gute Lösung zu sein. Viele WebAPI + OData-Tutorials haben Beispiele, in denen $ expand für mehrere Entitäten mit Schlüsseln aufgerufen wird. – MWinstead

4

Dies liegt daran, Sie sind

builder.EnableLowerCamelCase(); 

in Ihrem ODataConventionModelBuilder Setup.

Es erkennt "Owner" in Ihren Abfrageoptionen $ expand-Klausel nicht, da dieser Pfad wirklich nicht im OData-Modell existiert, da es die Groß- und Kleinschreibung unterscheidet.

Wenn Sie versuchen, diese/Hunde?$ expand = Besitzer Ich bin mir sicher, dass das funktioniert und du wirst sowohl Hunde als auch ihre Besitzer in der JSON-Antwort erhalten.

+0

Sorry - ich weiß, das ist ärgerlich, aber das ist auch nicht der Fall. :) Mit $ expand = Besitzer ändert sich nichts. Jetzt, da ich die [Queryable] -Attribute in [EnableQuery] auf meinen Controller-Methoden geändert habe, verursacht die Verwendung von expand einen Fehler: "Die im URI angegebene Abfrage ist nicht gültig. Beim Typ konnte keine Eigenschaft namens" Owner "gefunden werden "DogHotelAPI.Models.Dog". " – MWinstead

+0

Darüber hinaus würde ich nicht erwarten, dass dies ein Problem mit dem Format der Erweiterung ist, da das 'Besitzer' Feld NIEMALS auf Hund entities zurückkommt. Ich würde erwarten, dass selbst eine einfache Abfrage für alle Hunde die "Eigentümer" -Entität auf ihnen zurückgeben würde, selbst wenn es null ist. Das passiert nicht - "Besitzer" ist nie in einer zurückgegebenen Hundeeinheit vorhanden. – MWinstead

9

fand ich die Lösung für mein Problem, das letztlich durch drei Dinge verursacht wurde:

1.) Ich war mit den [Abfragbare] -Attribut auf meinen Controller-Methoden, die veraltet sind. Ich musste die neueren [EnableQuery] Attribute verwenden.

2.) In meiner WebApiConfig.cs-Datei habe ich die Abfrage mit dem Standard config.EnableQuerySupport() aktiviert. Dies ist veraltet und wurde entfernt entfernt.

3.12 Mein erweiterter Aufruf benötigt wurde in Form von $ expand = Besitzer, aber musste in der Form $ expand = Besitzer seit ich aktivieren unteren Kamelfall auf meinem ODataConventionModelBuilder. Vielen Dank an Mark Bennetts, dessen Antwort darauf hingewiesen hat!

Nachdem alle diese Änderungen vorgenommen wurden, werden zugehörige Entitäten des Besitzers mit Dog-Entitäten zurückgegeben.

Verwandte Themen