.Net Core 3.1 Web Api 的自定义 OData 日期时间序列化程序
Posted
技术标签:
【中文标题】.Net Core 3.1 Web Api 的自定义 OData 日期时间序列化程序【英文标题】:Custom OData DateTime Serializer for .Net Core 3.1 Web Api 【发布时间】:2021-02-10 01:30:22 【问题描述】:我有一个带有数据模型对象的 OData Api,该对象带有许多可为空的 DateTime 字段。
例如
public class Book : EntityBase
...
public DateTime? CreatedDate get; set;
public DateTime? UpdatedDate get; set;
...
使用 OData API 的客户端要求将 DateTime 字段格式化为“yyyy-MM-dd”格式,而不是像“yyyy-MM-ddTHH:mm:ss”这样的默认长格式
public class CustomODataSerializerProvider : DefaultODataSerializerProvider
private readonly CustomODataEntityTypeSerializer _entityTypeSerializer;
public CustomODataSerializerProvider(IServiceProvider rootContainer)
: base(rootContainer)
_entityTypeSerializer = new CustomODataEntityTypeSerializer(this);
public override ODataEdmTypeSerializer GetEdmTypeSerializer(Microsoft.OData.Edm.IEdmTypeReference edmType)
if (edmType.Definition.TypeKind == EdmTypeKind.Entity || edmType.Definition.TypeKind == EdmTypeKind.Complex)
return _entityTypeSerializer;
else
return base.GetEdmTypeSerializer(edmType);
public class CustomODataEntityTypeSerializer : ODataResourceSerializer
public CustomODataEntityTypeSerializer(ODataSerializerProvider provider)
: base(provider)
public override Microsoft.OData.ODataProperty CreateStructuralProperty(Microsoft.OData.Edm.IEdmStructuralProperty structuralProperty, ResourceContext resourceContext)
var property = base.CreateStructuralProperty(structuralProperty, resourceContext);
if (property.Name.Contains("Date"))
property.Value = ((DateTime)property.Value).ToShortDateString();
return property.Value != null ? property : null;
我也试过“property.Value = ((DateTimeOffset)property.Value).DateTime.ToShortDateString();”而不是上面的。
然后使用注册此序列化程序
app.UseEndpoints(endpoints =>
endpoints.MapControllers();
endpoints.EnableDependencyInjection();
endpoints.Select().Expand().OrderBy().Filter().Count().MaxTop(10);
endpoints.MapODataRoute("odata", "odata", a =>
a.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(IEdmModel), sp => GetEdmModel(app.ApplicationServices));
a.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(ODataSerializerProvider), sp => new CustomODataSerializerProvider(sp));
);
//endpoints.MapODataRoute("odata", "odata", GetEdmModel(app.ApplicationServices));
);
但是当调用 OData 端点时出现此错误
can't parse JSON. Raw result:
"@odata.context":"https://localhost:5000/odata/$metadata#Book","value":[
我也尝试应用 Json 序列化程序,但这对从 OData 端点提供的数据没有影响:
services
.AddControllers()
.AddJsonOptions(options =>
options.JsonSerializerOptions.IgnoreNullValues = false;
options.JsonSerializerOptions.Converters.Add(new DateTimeConverter());
)
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
public class DateTimeConverter : JsonConverter<DateTime>
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
Debug.Assert(typeToConvert == typeof(DateTime));
return DateTime.Parse(reader.GetString());
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy-MM-dd"));
我在 .NET Core 3.1 Web Api 中使用 Microsoft.AspNetCore.OData 7.4.1。任何有关如何更改通过 OData API 提供的数据的 DateTime 格式/序列化的建议将不胜感激。
【问题讨论】:
【参考方案1】:在声明您的 EDM 时,您可以使用 Edm.Date
格式标记要反序列化的特定字段,例如:
public static IEdmModel GetEdmModel()
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EnableLowerCamelCase();
builder.EntitySet<Book>("Book");
builder.EntityType<Book>().Property(p => p.CreatedDate).AsDate();
builder.EntityType<Book>().Property(p => p.UpdatedDate).AsDate();
return builder.GetEdmModel();
对于一个简单的控制器,如下所示:
namespace WebAPI.Controllers
[ApiController]
[Route("[controller]")]
public class BookController : ControllerBase
private static readonly List<Book> Books = new List<Book>
new Book()
BookId = 1,
CreatedDate = new DateTime(2020, 01, 02),
UpdatedDate = new DateTime(2020, 02, 03)
,
new Book()
BookId = 2,
CreatedDate = null,
UpdatedDate = null
,
;
[EnableQuery]
public IEnumerable<Book> Get()
var result = Books.ToArray();
return result;
您可以看到使用 YYYY-MM-DD 格式序列化的 DateTime?
字段:
"@odata.context": "https://localhost:5001/odata/$metadata#Book",
"value": [
"createdDate": "2020-01-02",
"updatedDate": "2020-02-03",
"bookId": 1
,
"createdDate": null,
"updatedDate": null,
"bookId": 2
]
并且 https://localhost:5001/odata/$metadata XML 将这些字段声明为Edm.Date
类型:
<edmx:Edmx Version="4.0">
<edmx:DataServices>
<Schema Namespace="WebAPI">
<EntityType Name="Book">
<Key>
<PropertyRef Name="bookId"/>
</Key>
<Property Name="createdDate" Type="Edm.Date"/>
<Property Name="updatedDate" Type="Edm.Date"/>
<Property Name="bookId" Type="Edm.Int32" Nullable="false"/>
</EntityType>
</Schema>
<Schema Namespace="Default">
<EntityContainer Name="Container">
<EntitySet Name="Book" EntityType="WebAPI.Book"/>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
【讨论】:
以上是关于.Net Core 3.1 Web Api 的自定义 OData 日期时间序列化程序的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 api 连接器和 asp net core web api 通过自定义声明丰富 azure b2c 令牌
NET Core 3.1 MVC 授权/身份验证,带有在单独的 Net Core 3.1 Web Api 中从外部获取的令牌 (JWT)
Wcf 服务在 .NET Core 3.1 控制台应用程序中工作,但在 ASP.NET Core 3.1 Web API 中无法工作