如何使用Microsoft.OData.Client和Web Api

时间:2016-05-22 17:19:24

标签: entity-framework asp.net-web-api odata odatacontroller

我有两个类VehicleOwnershipRecord,如果没有另一个类,它们都不能保存到数据库中。 Vehicle必须至少有一个OwnershipRecord,而OwnershipRecord必须与Vehicle相关联。否则就没有意义了。

使用Web Api和OData v4客户端代码生成器我还没有找到一种方法来序列化这两个对象并将它们一起发布。看来我需要发布一个车辆,然后添加一个OwnershipRecord或发布一个OwnershipRecord,然后添加一个车辆,这是不可能的。

DataServiceContext.AddObject提供以下内容:

将对象放入已添加状态的DataServiceContext的跟踪集中。 DataServiceContext将尝试在下次调用SaveChanges时通过HTTP POST插入对象。此方法不会将与指定实体相关的对象添加到DataServiceContext。必须通过单独调用AddObject添加每个对象。

该方法不验证指定的实体集是否在与DataServiceContext关联的数据服务中,或者验证添加的对象是否需要将所需的属性添加到指定的实体集中。

因此,传递时所有导航属性都为null。因此,当我将OwnershipRecord添加到newVehicle然后调用Container.AddToVehicles(newVehicle)时,VehiclesController上的POST方法会将ModelState.IsValid呈现为假Vehicle must have an owner!

如何使用客户端代码向车辆发送导航属性并将两者一起添加?我尝试在AddLink上使用AddRelatedObjectContainer,但它不适用于相对网址,因为这些项目尚不存在。

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

这是我的WebApiConfig.cs和我的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;
    }
}

==更新==

目前,我正在使用JsonConvert序列化有效负载并使用WebClient自行发送。我已从服务器中删除了所有ModelState逻辑。似乎目前不支持在客户端代码中包含导航属性。我可能不完全理解拦截batch命令,因为看起来我可以使用expand进行GET,我应该可以使用类似于expand的内容来进行POST

2 个答案:

答案 0 :(得分:0)

使用$ batch请求在同一个变更集中发送两个帖子。

请参阅此示例项目,了解如何支持$ batch,https://github.com/OData/ODataSamples/tree/master/WebApi/v4/ODataBatchSample

答案 1 :(得分:0)

所以我意识到要将两个参数一起发送,我可以在WebApiConfig.cs

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

控制器

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

客户

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