如何使用 Microsoft.OData.Client 和 Web Api 使用导航属性进行 POST

问题描述 投票:0回答:3

我有两个类,

Vehicle
OwnershipRecord
,没有一个不能持久保存到数据库中。一个
Vehicle
必须至少有一个
OwnershipRecord
并且一个
OwnershipRecord
必须与一个
Vehicle
相关联。否则没有意义。

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

DataServiceContext.AddObject
提供以下内容:

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

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

因此,所有导航属性在传递时都为空。因此,当我将

OwnershipRecord
s 添加到 newVehicle 然后调用
Container.AddToVehicles(newVehicle)
时,
VehiclesController
上的 POST 方法将
ModelState.IsValid
呈现为错误的说法
Vehicle must have an owner!
.

如何使用客户端代码发送具有导航属性的车辆并将两者加在一起?我尝试在

AddLink
上使用
AddRelatedObject
Container
但它不适用于相对 url,因为这些项目尚不存在。

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

entity-framework asp.net-web-api odata odatacontroller
3个回答
0
投票

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

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


0
投票

所以我意识到一起发送两个参数我可以在

WebApiConfig.cs

中使用一个动作
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();
        }

0
投票

有一个解决方法是使用 SetLink 这是一个例子:

var context = new DataServiceContext(new Uri("http://odataServiceUri"));
var customer = new Customer { Name = "John Doe" };
var account = new Account { Balance = 100 };
context.AddObject("Customers", customer);
context.AddObject("Accounts", account);
context.SetLink(customer, "Account", account);
context.SaveChanges();
© www.soinside.com 2019 - 2024. All rights reserved.