c# - 如何在 OData C# 驱动程序中支持嵌套的开放复杂类型?
问题描述
我在 .NET Web Api 项目中使用以下 C# OData 包:
安装包 Microsoft.AspNet.OData
安装包 Microsoft.AspNet.WebApi.OData
在遵循 Microsoft 的示例Use Open Types in OData v4时,只要开放类型不包含其他嵌套的开放复杂类型,一切似乎都按预期工作。
这意味着这将正常工作:
public class WplController : ODataController
{
private List<AbstractMongoDocument> _documents = new List<AbstractMongoDocument>
{
new AbstractMongoDocument
{
Id = "2",
Meta = new MongoMeta(),
Data = new MongoData
{
Document = new Dictionary<string, object>()
{
{"root_open_type", "This works!" },
}
}
}
};
[EnableQuery]
public IQueryable<AbstractMongoDocument> Get()
{ return _documents.AsQueryable();}
}
虽然这会引发异常
public class WplController : ODataController
{
private List<AbstractMongoDocument> _documents = new List<AbstractMongoDocument>
{
new AbstractMongoDocument
{
Id = "1",
Meta = new MongoMeta(),
Data = new MongoData
{
Document = new Dictionary<string, object>()
{
{"root_open_type", "This works!" },
{"nested_open_type", new Dictionary<string, object>() //Nested dictionary throws exception!
{
{"field1", "value2" },
{"field2", "value2" }
}
}
}
}
}
};
[EnableQuery]
public IQueryable<AbstractMongoDocument> Get()
{ return _documents.AsQueryable();}
}
例外情况如下:
System.InvalidOperationException 发生
消息:“ObjectContent`1”类型无法序列化内容类型“application/json”的响应正文;odata.metadata=minimal'。
消息:抛出异常:System.Web.OData.dll 中的“System.InvalidOperationException”
附加信息:给定模型不包含类型“System.Collections.Generic.Dictionary`2[System.String,System.Object]”。
这可以通过将以下行添加到ODataConventionModelBuilder
in来解决WebApiConfig.cs
:
builder.ComplexType<Dictionary<string, object>>();
但是,这会导致以下 OData 响应 JSON:
{
"@odata.context": "http://localhost:50477/odata/$metadata#wpl",
"value":
[
{
"Id": "1",
"Meta": {},
"Data":
{
"root_open_type": "This works!",
"nested_open_type":
{
"@odata.type": "#System.Collections.Generic.Dictionary_2OfString_Object",
"Keys":
[
"field1",
"field2"
]
}
}
}
]
}
如何确保 ODate 也正确序列化嵌套的开放字段?即我想要以下生成的 OData JSON:
{
"@odata.context": "http://localhost:50477/odata/$metadata#wpl",
"value":
[
{
"Id": "1",
"Meta": {},
"Data":
{
"root_open_type": "This works!",
"nested_open_type":
{
"field1": "value1",
"field2": "value2"
}
}
}
]
}
提前感谢任何潜在的帮助!
解决方案
我和你在同一条船上。我需要将一些数据公开为纯 JSON。这是使用ODataUntypedValue
该类的有效解决方案。它序列化为您所期望的。我用你的模型和我的模型测试了它。
实现一个MongoDataSerializer
类:
public class MongoDataSerializer: ODataResourceSerializer
{
public MongoDataSerializer(ODataSerializerProvider serializerProvider)
: base(serializerProvider)
{
}
/// <summary>
/// Serializes the open complex type as an <see cref="ODataUntypedValue"/>.
/// </summary>
/// <param name="graph"></param>
/// <param name="expectedType"></param>
/// <param name="writer"></param>
/// <param name="writeContext"></param>
public override void WriteObjectInline(
object graph,
IEdmTypeReference expectedType,
ODataWriter writer,
ODataSerializerContext writeContext)
{
// This cast is safe because the type is checked before using this serializer.
var mongoData = (MongoData)graph;
var properties = new List<ODataProperty>();
foreach (var item in mongoData.Document)
{
properties.Add(new ODataProperty
{
Name = item.Key,
Value = new ODataUntypedValue
{
RawValue = JsonConvert.SerializeObject(item.Value),
},
});
}
writer.WriteStart(new ODataResource
{
TypeName = expectedType.FullName(),
Properties = properties,
});
writer.WriteEnd();
}
}
实现一个CustomODataSerializerProvider
类:
public class CustomODataSerializerProvider : DefaultODataSerializerProvider
{
private readonly MongoDataSerializer mongoDataSerializer;
public CustomODataSerializerProvider(
IServiceProvider odataServiceProvider)
: base(odataServiceProvider)
{
this.mongoDataSerializer = new MongoDataSerializer(this);
}
public override ODataEdmTypeSerializer GetEdmTypeSerializer(IEdmTypeReference edmType)
{
if (edmType.FullName() == typeof(MongoData).FullName)
{
return this.mongoDataSerializer;
}
return base.GetEdmTypeSerializer(edmType);
}
}
在CustomODataSerializerProvider
您的Startup.cs
:
app.UseMvc(options =>
{
var model = builder.GetEdmModel();
options
.MapODataServiceRoute(
"odata",
"odata",
b => b
.AddService(Microsoft.OData.ServiceLifetime.Scoped, s => model)
.AddService<IEnumerable<IODataRoutingConvention>>(
Microsoft.OData.ServiceLifetime.Scoped,
s => ODataRoutingConventions.CreateDefaultWithAttributeRouting("odata", options))
.AddService<ODataSerializerProvider, CustomODataSerializerProvider>(Microsoft.OData.ServiceLifetime.Singleton));
}
这是使用您的模型的输出(注意属性名称以小写字母开头,因为我启用了ODataConventionModelBuilder.EnableLowerCamelCase()
):
推荐阅读
- python - 如何在python中以两种不同的颜色显示来自两个标签(0,1)的数值?
- ag-grid - ag-Grid 中的警告:您正在混合模块(即@ag-grid-community/core)和包(ag-grid-community)
- javascript - 如何使用 Sharp 从 S3 调整图像大小?
- javascript - PHP 从下拉列表中选择
- python - 知道用户何时单击鼠标导致死锁的脚本
- elixir - 你能用块超级覆盖凤凰模板部分吗(比如Django?)
- angular - 重构 Promise 代码以在新的 Promise 构造函数中删除 async/await
- c - 在 /bin 中搜索带有 stat 的文件
- x86 - 为什么“movnti”后跟“sfence”保证持久排序?
- c# - 如何解决“调用目标引发异常”。这个错误