问题描述
我在.NET Web Api项目中使用以下C#OData软件包:
在遵循Microsoft的示例时
I am using the following C# OData packages, in a .NET Web Api project:
When following Microsoft's example Use Open Types in OData v4, everything seems to work as expected, as long as the open type does not contain additional nested open complex types.
This means that this will work fine:
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();}
}
While this throws an exception
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();}
}
The exception is as follows:
This can be fixed by adding the following line to the ODataConventionModelBuilder
in WebApiConfig.cs
:
builder.ComplexType<Dictionary<string, object>>();
However, this leads to the following OData response 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"
]
}
}
}
]
}
How can I make sure that ODate properly serializes the nested open fields as well? I.e. I would like the following resulting 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"
}
}
}
]
}
Thanks in advance for any potential help!
I'm in the same boat as you. I need to expose some data as pure JSON. This is a working solution using the ODataUntypedValue
class. It serializes to what you would expect. I tested it with your models and mine.
Implement a MongoDataSerializer
class:
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();
}
}
Implement a CustomODataSerializerProvider
class:
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);
}
}
Register the CustomODataSerializerProvider
in your 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));
}
This is the output using your models (note the property names begin with a lower case letter because I enabled ODataConventionModelBuilder.EnableLowerCamelCase()
):
这篇关于如何在OData C#驱动程序中支持嵌套的开放复杂类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!