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