Я работаю над ASP.NET Core 2.2 API, который реализует OData через Microsoft.AspNetCore.Odata v7.1.0 NuGet. У меня все работало нормально, поэтому я решил добавить управление версиями API через Microsoft.AspNetCore.OData.Versioning v3.1.0.
Теперь мои методы GET и GET {id} в моем контроллере корректно работают с версиями. Например, я могу добраться до метода конечной точки списка GET, используя URL
~/api/v1/addresscompliancecodes
или
~/api/addresscompliancecodes?api-version=1.0
Однако, когда я пытаюсь создать новую запись, запрос перенаправляется на правильный метод в контроллере, но теперь содержимое тела запроса не передается методу POST-контроллера
Я следовал примерам в Microsoft.ApsNetCore.OData.Versioning GitHub
В моем контроллере есть метод HttpPost;
[HttpPost]
[ODataRoute()]
public async Task<IActionResult> CreateRecord([FromBody] AddressComplianceCode record, ODataQueryOptions<AddressComplianceCode> options)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_context.Add(record);
await _context.SaveChangesAsync();
return Created(record);
}
Когда я отлаживаю, запрос перенаправляется к методу контроллера правильно, но переменная «запись» теперь пуста, тогда как перед добавлением изменений кода для управления версиями API он был правильно заполнен.
Я подозреваю, что именно так я использую конструктор моделей, поскольку этот код был изменен для поддержки управления версиями API.
Прежде чем пытаться реализовать управление версиями API, я использовал класс построителя моделей, как показано ниже;
public class AddressComplianceCodeModelBuilder
{
public IEdmModel GetEdmModel(IServiceProvider serviceProvider)
{
var builder = new ODataConventionModelBuilder(serviceProvider);
builder.EntitySet<AddressComplianceCode>(nameof(AddressComplianceCode))
.EntityType
.Filter()
.Count()
.Expand()
.OrderBy()
.Page() // Allow for the $top and $skip Commands
.Select();
return builder.GetEdmModel();
}
}
И метод Startup.cs -> Configure, как показано в приведенном ниже фрагменте кода;
// Support for OData $batch
app.UseODataBatching();
app.UseMvc(routeBuilder =>
{
// Add support for OData to MVC pipeline
routeBuilder
.MapODataServiceRoute("ODataRoutes", "api/v1",
modelBuilder.GetEdmModel(app.ApplicationServices),
new DefaultODataBatchHandler());
});
И он работал с [FromBody] в методе HttpPost контроллера.
Однако, следуя примерам в OData GitHub управления версиями API, я теперь использую класс конфигурации, подобный тому, что показан ниже, а не конструктор моделей из предыдущих;
public class AddressComplianceCodeModelConfiguration : IModelConfiguration
{
private static readonly ApiVersion V1 = new ApiVersion(1, 0);
private EntityTypeConfiguration<AddressComplianceCode> ConfigureCurrent(ODataModelBuilder builder)
{
var addressComplianceCode = builder.EntitySet<AddressComplianceCode>("AddressComplianceCodes").EntityType;
addressComplianceCode
.HasKey(p => p.Code)
.Filter()
.Count()
.Expand()
.OrderBy()
.Page() // Allow for the $top and $skip Commands
.Select();
return addressComplianceCode;
}
public void Apply(ODataModelBuilder builder, ApiVersion apiVersion)
{
if (apiVersion == V1)
{
ConfigureCurrent(builder);
}
}
}
И мой метод Startup.cs -> Configure был изменен, как показано ниже;
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
VersionedODataModelBuilder modelBuilder)
{
// Support for OData $batch
app.UseODataBatching();
app.UseMvc(routeBuilder =>
{
// Add support for OData to MVC pipeline
var models = modelBuilder.GetEdmModels();
routeBuilder.MapVersionedODataRoutes("odata", "api", models);
routeBuilder.MapVersionedODataRoutes("odata-bypath", "api/v{version:apiVersion}", models);
});
}
Если это уместно, у меня в Startup.cs есть следующий код -> ConfigureServices;
// Add Microsoft's API versioning
services.AddApiVersioning(options =>
{
// reporting api versions will return the headers "api-supported-versions" and "api-deprecated-versions"
options.ReportApiVersions = true;
});
// Add OData 4.0 Integration
services.AddOData().EnableApiVersioning();
services.AddMvc(options =>
{
options.EnableEndpointRouting = false; // TODO: Remove when OData does not causes exceptions anymore
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddJsonOptions(opt =>
{
opt.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
});
Мне кажется, проблема в том, что модель как-то не совпадает, но я не могу точно понять, почему это не так
ОБНОВЛЕНИЕ 3/18/19 - Дополнительная информация
Вот мой класс сущности;
[Table("AddressComplianceCodes")]
public class AddressComplianceCode : EntityBase
{
[Key]
[Column(TypeName = "char(2)")]
[MaxLength(2)]
public string Code { get; set; }
[Required]
[Column(TypeName = "varchar(150)")]
[MaxLength(150)]
public string Description { get; set; }
}
и класс EntityBase;
public class EntityBase : IEntityDate
{
public bool MarkedForRetirement { get; set; }
public DateTimeOffset? RetirementDate { get; set; }
public DateTimeOffset? LastModifiedDate { get; set; }
public string LastModifiedBy { get; set; }
public DateTimeOffset? CreatedDate { get; set; }
public string CreatedBy { get; set; }
public bool Delete { get; set; }
public bool Active { get; set; }
}
А вот тело запроса от Почтальона;
{
"@odata.context": "https://localhost:44331/api/v1/$metadata#AddressComplianceCodes",
"Code": "Z1",
"Description": "Test Label - This is a test for Z1",
"Active": true
}
Есть идеи?