我正在使用最新的OData NuGet软件包编写OData V4服务/ Web API 2。我有一个问题,我认为是服务器上的格式问题或配置问题,但是我是OData和WebAPI的新手,所以我可能是错的。
问题是这样的: 如果我用补丁程序调用OData服务,其中在指向声明的属性的字段和动态属性中都包含非美国字符串(例如“Mølgaard”),则在控制器中的“Mølgaard”控制器中进入我的patch方法声明的属性,但是在动态属性中,我获得了原始值“ M \ u00f8lgaard”。预期的行为是同时获得“Mølgaard”,我发现非常奇怪的是,动态属性的处理方式似乎与声明的POCO属性不同。 我已经使用绑定到我的服务的生成的MS ODataClient并通过称为Postman的工具尝试了此操作,在两种情况下,我都使用相同的错误值进入Patch方法。
作为OData的新手,我尝试按如下所述添加新的序列化器: http://odata.github.io/WebApi/#06-03-costomize-odata-formatter 并按照此处答案中的描述进行修改: OData WebApi V4 .net - Custom Serialization 我还找到了有关使用Newtonsoft.Json.JsonConvert的示例。 简而言之,两者都无济于事,我想这实际上都不是要解决这个问题。
我从在这里找到的演示项目开始: https://github.com/DevExpress-Examples/XPO_how-to-implement-odata4-service-with-xpo
我像这样添加了POCO类:
public class OOrder : IDynamicProperties
{
[System.ComponentModel.DataAnnotations.Key]
public int ID { get; set; }
// SomeText is the declared property and its value
// is then repeated in DynamicProperties with another name
public string SomeText { get; set; }
public IDictionary<string, object> DynamicProperties { get; set; }
}
// I do not know if I need this, I am using
// it in a map function
public interface IDynamicProperties
{
IDictionary<string, object> DynamicProperties { get; set; }
}
我的配置非常基本:
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
config.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
ODataModelBuilder modelBuilder = CreateODataModelBuilder();
ODataBatchHandler batchHandler = new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer);
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: modelBuilder.GetEdmModel(),
batchHandler: batchHandler);
}
static ODataModelBuilder CreateODataModelBuilder()
{
ODataModelBuilder builder = new ODataModelBuilder();
var openOrder = builder.EntityType<OOrder>();
openOrder.HasKey(p => p.ID);
openOrder.Property(p => p.SomeText);
openOrder.HasDynamicProperties(p => p.DynamicProperties);
builder.EntitySet<OOrder>("OOrders");
return builder;
}
}
我在控制器上的补丁功能如下:
[HttpPatch]
public IHttpActionResult Patch([FromODataUri] int key, Delta<OOrder> order)
{
if (!ModelState.IsValid) return BadRequest();
using (UnitOfWork uow = ConnectionHelper.CreateSession()) {
OOrder existing = getSingle(key, uow);
if (existing != null) {
Order existingOrder = uow.GetObjectByKey<Order>(key);
order.CopyChangedValues(existing);
mapOpenWithDynamcPropertiesToPersisted(existing, existingOrder);
// Intentionally not storing changes for now
//uow.CommitChanges();
return Updated(existing);
}
else {
return NotFound();
}
}
}
private void mapOpenWithDynamcPropertiesToPersisted<TOpen, TPersisted>(TOpen open, TPersisted persisted)
where TPersisted : BaseDocument
where TOpen: IDynamicProperties {
if (open != null && persisted != null && open.DynamicProperties != null && open.DynamicProperties.Any()) {
XPClassInfo ci = persisted.ClassInfo;
foreach (string propertyName in open.DynamicProperties.Keys) {
var member = ci.FindMember(propertyName);
if (member != null) {
object val = open.DynamicProperties[propertyName];
// Here, I have tried to deserialize etc
member.SetValue(persisted, val);
}
}
}
}
调用order.CopyChangedValues(existing)之后,现有实例的“ SomeText”属性中包含正确编码的值,但相应的Dynamic属性中没有。
答案 0 :(得分:1)
我找到了一个答案,这显然与我自己没有正确阅读我在问题中提到的文章有关。 答案似乎是一个注入式解串器,它使用json转换器转换动态属性,因为它们显然总是以原始格式 。 我的配置现在是这样的:
public static void Register(HttpConfiguration config) {
config.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
ODataBatchHandler batchHandler = new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer);
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
configureAction: builder => builder.AddService<IEdmModel>(ServiceLifetime.Singleton, sp => CreateODataModel())
.AddService<ODataBatchHandler>(ServiceLifetime.Singleton, bb => new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer))
.AddService<IEnumerable<IODataRoutingConvention>>(ServiceLifetime.Singleton, sp => ODataRoutingConventions.CreateDefaultWithAttributeRouting("ODataRoute", config))
.AddService<Microsoft.AspNet.OData.Formatter.Serialization.ODataSerializerProvider>(ServiceLifetime.Singleton, sp => new MiTestSerializerProvider(sp))
.AddService<Microsoft.AspNet.OData.Formatter.Deserialization.ODataDeserializerProvider>(ServiceLifetime.Singleton, sp => new MiDynamicPropertiesDeserializerProvider(sp))
);
}
在这种情况下,反序列化器是重要的。 我开始使用反序列化器的样子(需要提供程序/实现耦合):
public class MiDynamicPropertiesDeserializerProvider : DefaultODataDeserializerProvider
{
MiDynamicPropertiesDeserializer _edmSerializer;
public MiDynamicPropertiesDeserializerProvider(IServiceProvider rootContainer) : base(rootContainer) {
_edmSerializer = new MiDynamicPropertiesDeserializer(this);
}
public override ODataEdmTypeDeserializer GetEdmTypeDeserializer(IEdmTypeReference edmType) {
switch (edmType.TypeKind()) { // Todo: Do I need more deserializers ?
case EdmTypeKind.Entity: return _edmSerializer;
default: return base.GetEdmTypeDeserializer(edmType);
}
}
}
public class MiDynamicPropertiesDeserializer : ODataResourceDeserializer {
public MiDynamicPropertiesDeserializer(ODataDeserializerProvider serializerProvider) : base(serializerProvider) { }
private static Dictionary<Type, Func<object, object>> simpleTypeConverters = new Dictionary<Type, Func<object, object>>() {
{ typeof(DateTime), d => new DateTimeOffset((DateTime)d) } // Todo: add converters or is this too simple ?
};
public override void ApplyStructuralProperty(object resource, ODataProperty structuralProperty, IEdmStructuredTypeReference structuredType, ODataDeserializerContext readContext) {
if (structuralProperty != null && structuralProperty.Value is ODataUntypedValue) {
// Below is a Q&D mapper I am using in my test to represent properties
var tupl = WebApplication1.Models.RuntimeClassesHelper.GetFieldsAndTypes().Where(t => t.Item1 == structuralProperty.Name).FirstOrDefault();
if (tupl != null) {
ODataUntypedValue untypedValue = structuralProperty.Value as ODataUntypedValue;
if (untypedValue != null) {
try {
object jsonVal = JsonConvert.DeserializeObject(untypedValue.RawValue);
Func<object, object> typeConverterFunc;
if (jsonVal != null && simpleTypeConverters.TryGetValue(jsonVal.GetType(), out typeConverterFunc))
{
jsonVal = typeConverterFunc(jsonVal);
}
structuralProperty.Value = jsonVal;
}
catch(Exception e) { /* Todo: handle exceptions ? */ }
}
}
}
base.ApplyStructuralProperty(resource, structuralProperty, structuredType, readContext);
}
}
感谢所有花时间在此上的人,我希望其他人会发现此信息有用。