我在.NET Web Api项目中使用以下C#OData包:
安装 - 打包Microsoft.AspNet.OData
安装包Microsoft.AspNet.WebApi.OData
当遵循Microsoft的示例Use Open Types in OData v4时,只要open类型不包含其他嵌套的开放复杂类型,一切似乎都按预期工作。
这意味着这样可以正常工作:
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 =最小”。
消息:抛出异常:System.Web.OData.dll中的“System.InvalidOperationException”
附加信息:给定的模型不包含类型'System.Collections.Generic.Dictionary`2 [System.String,System.Object]'。
可以通过将以下行添加到ODataConventionModelBuilder
中的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"
}
}
}
]
}
提前感谢任何潜在的帮助!
答案 0 :(得分:3)
我和你在同一条船上。我需要将一些数据公开为纯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()
):
答案 1 :(得分:0)
将Dictionary<string, object>
添加为开放的复杂类型可能会使构建器感到困惑。开放类型需要定义一个包含动态属性的属性。在Dictionary<string, object>
的情况下,似乎认为Keys
集合是开放类型的动态属性容器。尝试创建一个类型定义为复杂类型,如下所示:
public class OpenComplexType
{
public IDictionary<string, object> DynamicProperties { get; set; }
}
然后将其注册为复杂类型:
builder.ComplexType<OpenComplexType>();
最后,使用Document
作为类型来定义您的OpenComplexType
属性:
public class MongoData
{
public OpenComplexType Document { get; set; }
}
我绝不是WebAPI OData库的专家,使用Dictionary<string, object>
可能还有其他方法可以解决此问题,但这应该是一个起点。