我有两个类,没有另一个类Vehicle
,OwnershipRecord
两者都不能持久化到数据库中。AVehicle
必须至少有一个OwnershipRecord
,并且 anOwnershipRecord
必须与 a 相关联Vehicle
。否则没有意义。
使用 Web Api 和 OData v4 客户端代码生成器我还没有想出一种方法来序列化两个对象并将它们一起发布。看来我需要发布车辆然后添加 OwnershipRecord 或发布 OwnershipRecord 然后添加车辆,这是不可能的。
DataServiceContext.AddObject
提供以下内容:
对象被放入处于已添加状态的 DataServiceContext 的跟踪集中。DataServiceContext 将在下次调用 SaveChanges 时尝试通过 HTTP POST 插入对象。此方法不会将与指定实体相关的对象添加到 DataServiceContext。必须通过单独调用 AddObject 来添加每个对象。
该方法不验证指定的实体集是否在与 DataServiceContext 关联的数据服务中,或者添加的对象是否具有需要添加到指定实体集的所需属性。
因此,所有导航属性在传递时都为空。因此,当我将OwnershipRecord
s 添加到 newVehicle 然后调用Container.AddToVehicles(newVehicle)
时,呈现的 POST 方法VehiclesController
会呈现ModelState.IsValid
为错误的说法Vehicle must have an owner!
。
如何使用客户端代码发送带有导航属性的车辆并将两者相加?我尝试使用AddLink
and 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 的东西