2016-05-22 18 views
0

Ich habe zwei Klassen, Vehicle und OwnershipRecord und beide können nicht ohne die andere in der Datenbank gespeichert werden. A Vehicle muss mindestens eine OwnershipRecord haben und eine OwnershipRecord muss mit einer Vehicle verbunden sein. Es macht sonst keinen Sinn.Wie kann ich POST mit Navigationseigenschaften mit Microsoft.OData.Client und Web API

Mit Web Api und OData v4 Client Code Generator Ich habe keine Möglichkeit gefunden, beide Objekte zu serialisieren und sie zusammen zu POST. Es sieht so aus, als müsste ich ein Fahrzeug POSTIEREN und dann einen OwnershipRecord hinzufügen oder einen OwnershipRecord posten und dann ein Fahrzeug hinzufügen, was nicht möglich ist.

DataServiceContext.AddObject bietet die folgenden:

Das Objekt wird gesetzt in den Tracking-Satz des Dataservicecontext im Added Zustand. Der DataServiceContext versucht, das Objekt beim nächsten Aufruf von SaveChanges per HTTP POST einzufügen. Diese Methode fügt dem DataServiceContext keine Objekte hinzu, die sich auf die angegebene Entität beziehen. Jedes Objekt muss durch einen separaten Aufruf von AddObject hinzugefügt werden.

Das Verfahren überprüft nicht, dass die angegebenen Entitätsmenge in dem Datendienst mit dem Dataservicecontext oder dass das hinzugefügte Objekt hat die erforderlichen Eigenschaften benötigt werden, hinzugefügt, um den angegebenen Entitätsmenge ..

Daher sind alle verbunden ist, Navigationseigenschaften sind null, wenn sie übergeben werden. Wenn ich also OwnershipRecord s zu newVehicle hinzufüge und dann Container.AddToVehicles(newVehicle) rufe, rendert die POST-Methode auf ModelState.IsValid als falsches Sprichwort Vehicle must have an owner!.

Wie kann ich den Client-Code verwenden, um das Fahrzeug mit seiner Navigationseigenschaft zu senden und die beiden zusammen hinzuzufügen? Ich habe versucht, AddLink und AddRelatedObject auf dem Container zu verwenden, aber es funktioniert nicht mit relativen URLs, weil die Einzelteile noch nicht existieren.

public class Vehicle : IValidatableObject 
{ 
    public const string MissingOwnerMessage = "Vehicle must have an owner!"; 

    public Vehicle() 
    { 
     OwnershipRecords = new HashSet<OwnershipRecord>(); 
    } 

    [Key] 
    public int Id { get; set; } 

    public virtual ICollection<OwnershipRecord> OwnershipRecords { get; set; } 

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
    { 
     if (OwnershipRecords.Count == 0) 
     { 
      yield return new ValidationResult(MissingOwnerMessage); 
     } 
    } 
} 

public class OwnershipRecord : IValidatableObject 
{ 
    public const string MissingOwnerMessage = "Owner is required when creating Ownership-Record!"; 
    public const string MissingVehicleMessage = "Vehicle is required when creating Ownership-Record!"; 

    public OwnershipRecord() 
    { 
     Owners = new HashSet<Entity>(); 
    } 

    [Key] 
    public int Id { get; set; } 

    [Required] 
    public int VehicleId { get; set; } 

    [ForeignKey("VehicleId")] 
    public virtual Vehicle Vehicle { get; set; } 

    public virtual ICollection<Entity> Owners { get; set; } 

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
    { 
     if (Owners.Count == 0) 
     { 
      yield return new ValidationResult(MissingOwnerMessage); 
     } 

     if (Vehicle == null) 
     { 
      yield return new ValidationResult(MissingVehicleMessage); 
     } 
    } 
} 

Hier ist meine WebApiConfig.cs und meine ODataControllers.

public static class WebApiConfig 
{ 
    public static void Register(HttpConfiguration config) 
    { 
     config.EnableEnumPrefixFree(true); 
     config.MapODataServiceRoute("nms", "nms", GetImplicitEdmModel(), new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer)); 

     config.EnsureInitialized(); 
    } 

    private static IEdmModel GetImplicitEdmModel() 
    { 
     ODataConventionModelBuilder builder = new ODataConventionModelBuilder(); 
     builder.EntitySet<Entity>("Entities"); 
     builder.EntitySet<Vehicle>("Vehicles"); 
     builder.EntitySet<OwnershipRecord>("OwnershipRecords"); 

     builder.Namespace = "LocationService"; 

     return builder.GetEdmModel(); 
    } 

[ODataRoutePrefix("OwnershipRecords")] 
public class OwnershipRecordsController : ODataController 
{ 
    private NirvcModelV2 db = new NirvcModelV2(); 

    // GET: odata/OwnershipRecords 
    [EnableQuery] 
    public IQueryable<OwnershipRecord> GetOwnershipRecords() 
    { 
     return db.OwnershipRecords; 
    } 

    // GET: odata/OwnershipRecords(5) 
    [EnableQuery] 
    public SingleResult<OwnershipRecord> GetOwnershipRecord([FromODataUri] int key) 
    { 
     return SingleResult.Create(db.OwnershipRecords.Where(ownershipRecord => ownershipRecord.Id == key)); 
    } 

    // POST: odata/OwnershipRecords 
    public IHttpActionResult Post(OwnershipRecord ownershipRecord) 
    { 
     if (!ModelState.IsValid) 
     { 
      return BadRequest(ModelState); 
     } 

     db.OwnershipRecords.Add(ownershipRecord); 

     return Created(ownershipRecord); 
    } 

    // GET: odata/OwnershipRecords(5)/Vehicle 
    [EnableQuery] 
    public SingleResult<Vehicle> GetVehicle([FromODataUri] int key) 
    { 
     return SingleResult.Create(db.OwnershipRecords.Where(m => m.Id == key).Select(m => m.Vehicle)); 
    } 

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

} 

[ODataRoutePrefix("Vehicles")] 
public class VehiclesController : ODataController 
{ 
    private NirvcModelV2 db = new NirvcModelV2(); 

    // GET: odata/Vehicles 
    [EnableQuery(MaxExpansionDepth = 0)] 
    public IQueryable<Vehicle> GetVehicles() 
    { 
     return db.Vehicles; 
    } 

    // GET: odata/Vehicles(5) 
    [EnableQuery(MaxExpansionDepth = 0)] 
    public SingleResult<Vehicle> GetVehicle([FromODataUri] int key) 
    { 
     return SingleResult.Create(db.Vehicles.Where(vehicle => vehicle.Id == key)); 
    } 

    // POST: odata/Vehicles 
    public IHttpActionResult Post(Vehicle vehicle) 
    { 
     if (!ModelState.IsValid) 
     { 
      return BadRequest(ModelState); 
     } 

     db.Vehicles.Add(vehicle); 
     db.SaveChanges(); 

     return Created(vehicle); 
    } 

    // PATCH: odata/Vehicles(5) 
    [AcceptVerbs("PATCH", "MERGE")] 
    public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Vehicle> patch) 
    { 
     Vehicle vehicle = await db.Vehicles.FindAsync(key); 
     if (vehicle == null) 
     { 
      return NotFound(); 
     } 

     patch.Patch(vehicle); 

     try 
     { 
      await db.SaveChangesAsync(); 
     } 
     catch (DbUpdateConcurrencyException) 
     { 
      if (!VehicleExists(key)) 
      { 
       return NotFound(); 
      } 
      else 
      { 
       throw; 
      } 
     } 

     return Updated(vehicle); 
    } 

    // GET: odata/Vehicles(5)/OwnershipRecords 
    [EnableQuery] 
    public IQueryable<OwnershipRecord> GetOwnershipRecords([FromODataUri] int key) 
    { 
     return db.Vehicles.Where(m => m.Id == key).SelectMany(m => m.OwnershipRecords); 
    } 

    [HttpPost] 
    [ODataRoute("({key})/OwnershipRecords")] 
    public async Task<IHttpActionResult> PostOwnershipRecord([FromODataUri] int key, OwnershipRecord ownershipRecord) 
    { 
     if (!ModelState.IsValid) 
     { 
      return BadRequest(ModelState); 
     } 

     db.OwnershipRecords.Add(ownershipRecord); 
     try 
     { 
      await db.SaveChangesAsync(); 
     } 
     catch (DBConcurrencyException) 
     { 
      throw; 
     } 

     return Created(ownershipRecord); 
    } 

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

    private bool VehicleExists(int key) 
    { 
     return db.Vehicles.Count(e => e.Id == key) > 0; 
    } 
} 

== == aktualisieren

Zur Zeit habe ich die Nutzlast mit JsonConvert bin Serialisierung und es selbst WebClient mit senden. Ich habe alle ModelState Logik vom Server entfernt. Es scheint, dass es keine aktuelle Unterstützung für die Aufnahme von Navigationseigenschaften in den Client-Code gibt. Ich kann nicht vollständig verstehen die batch Befehl abfangen, weil es scheint, wenn ich expand mit GET verwenden kann, sollte ich in der Lage sein, etwas zu verwenden, ähnlich wie expand für POST

Antwort

0

Verwenden $ Batch-Anfrage zwei Posten in der gleichen changeset zu senden.

Siehe diesem Beispielprojekt wie $ Batch unterstützt wird, https://github.com/OData/ODataSamples/tree/master/WebApi/v4/ODataBatchSample

+0

Von dem, was ich mit Batch versucht habe, versucht es immer noch einzeln zu speichern. Ich brauche den OwnershipRecord, der an die Vehicle-Server-Seite angehängt ist, so dass ein Aufruf von db.SaveChanges() funktioniert –

+0

Versuchen Sie Changeset zu verwenden, wird die Anforderung in demselben Changeset in einem Aufruf gespeichert und jede Anforderung und Changeset in Batchanforderung wird sequentiell pro Protokoll abgearbeitet. Sie können auf http://docs.oasis-open.org/odata/odata/v4.0/errata02/os/complete/part1-protocol/odata-v4.0-errata02-os-part1-protocol-complete verweisen. html # _Change_Sets, um das Beispiel der Anfrage zu sehen. – Vincent

+0

Ich denke mein Kommentar war unklar, das Problem ist die Validierung. Wenn ich 'container.SaveChanges (SaveChangesOptions.BatchWithSingleChangeset) 'verwende, sendet der Befehl einen Post für Vehicle, der fehlschlägt, und einen Post für OwnershipRecord, der fehlschlägt. Ich brauche auf der Serverseite, um beide Objekte zu empfangen, sie zu verbinden, und dann kann der Server SaveChanges() erfolgreich aufrufen –

0

So erkannte ich zwei Parameter senden zusammen konnte ich eine Aktion verwenden in WebApiConfig.cs

var newVehicleFunction = builder.EntityType<Entity>().Action("AddVehicle").ReturnsFromEntitySet<OwnershipRecord>("OwnershipRecords"); 
     newVehicleFunction.Parameter<Vehicle>("Vehicle"); 
     newVehicleFunction.Parameter<OwnershipRecord>("OwnershipRecord"); 

Der Regler

public IHttpActionResult PostVehicle([FromODataUri] int key, ODataActionParameters parameters) 
    { 
     if (!ModelState.IsValid) 
     { 
      return BadRequest(); 
     } 

     Vehicle vehicle = (Vehicle)parameters["Vehicle"]; 
     OwnershipRecord ownRecord = (OwnershipRecord)parameters["OwnershipRecord"]; 

     var owner = db.Entities.Find(key); 
     if (owner == null) 
     { 
      return NotFound(); 
     } 

     ownRecord.Vehicle = vehicle; 
     owner.OwnershipRecords.Add(ownRecord); 

     try 
     { 
      db.SaveChanges(); 
     } 
     catch (DbUpdateException e) 
     { 
      throw; 
     } 

     return Created(ownRecord); 
    } 

Der Kunde

 if (newVehicle) 
     { 
      nmsContainer.Entities.ByKey(ownerId).AddVehicle(vehicle, ownRecord).GetValue(); 
     }