给定一个文件
{
"shape": {
"_bsonName": "[DiscriminatorValue]",
"geoJson": ...
}
}
[DiscriminatorValue]
可以是以下之一:
每种不同的类型在 json 中都有自己的数据形状
基本上,mongo 通常用于鉴别器的 _t 字段需要是“_bsonName”,但我还必须读取 _bsonName 字段并返回我们的 C# 类型:
RectangleShape|EllipseShape|PolygonShape
所以我创建了一个 IDiscriminatorConvention 类
public class GeofenceShapeDiscriminatorConvention : IDiscriminatorConvention
{
public string ElementName => "_bsonName";
public Type GetActualType(IBsonReader bsonReader, Type nominalType)
{
var bookmark = bsonReader.GetBookmark();
string bsonName = null;
bsonReader.ReadStartDocument();
if(bsonReader.FindElement("_bsonName"))
{
bsonName = bsonReader.ReadString();
}
bsonReader.ReturnToBookmark(bookmark);
switch (bsonName)
{
case "Company::Mapping::Geofences::GeoEllipse":
return typeof(EllipseGeofenceShape);
case "Company::Mapping::Geofences::GeoRectangle":
return typeof(RectangleGeofenceShape);
case "Company::Mapping::Geofences::GeoPolygon":
return typeof(PolygonGeofenceShape);
default:
throw new NotSupportedException($"Unexpected shape._bsonName of {bsonName}.");
}
}
public BsonValue GetDiscriminator(Type nominalType, Type actualType)
{
switch(actualType)
{
case Type _ when actualType == typeof(EllipseGeofenceShape):
return "Company::Mapping::Geofences::GeoEllipse";
case Type _ when actualType == typeof(RectangleGeofenceShape):
return "Company::Mapping::Geofences::GeoRectangle";
case Type _ when actualType == typeof(PolygonGeofenceShape):
return "Company::Mapping::Geofences::GeoPolygon";
default:
throw new ApplicationException($"Unexpected type '{actualType.FullName}' when serializing.");
}
}
}
不幸的是,此代码失败并出现 StackOverflowException,其中 Mongo 代码不断一遍又一遍地调用 GetActualType。
查看了他们的源代码,没有很多合适的例子。
值得注意的是,如果我跳过
bsonReader.ReturnToBookmark(bookmark);
,那么我会得到一个不同的错误,基本上是failed to GetBsonType() on a node of type value
(我忘记了确切的措辞)
您可以从
StandardDiscriminatorConvention
派生并使用 BsonDiscriminator
属性,而不是从头开始编写鉴别器约定,例如:
internal class GeofenceDiscriminatorConvention : StandardDiscriminatorConvention
{
public GeofenceDiscriminatorConvention() : base("_bsonName")
{
}
public static void Register()
{
BsonSerializer.RegisterDiscriminatorConvention(typeof(GeofenceShape), new GeofenceDiscriminatorConvention());
}
public override BsonValue GetDiscriminator(Type nominalType, Type actualType)
{
switch (actualType)
{
case Type _ when actualType == typeof(EllipseGeofenceShape):
return "Company::Mapping::Geofences::GeoEllipse";
case Type _ when actualType == typeof(RectangleGeofenceShape):
return "Company::Mapping::Geofences::GeoRectangle";
case Type _ when actualType == typeof(PolygonGeofenceShape):
return "Company::Mapping::Geofences::GeoPolygon";
default:
throw new ApplicationException($"Unexpected type '{actualType.FullName}' when serializing.");
}
}
}
在
GeofenceShape
类上,您可以像这样设置相应的鉴别器值:
// Base class
[BsonKnownTypes(typeof(EllipseGeofenceShape), typeof(PolygonGeofenceShape), typeof(RectangleGeofenceShape))]
internal class GeofenceShape
{
[BsonRepresentation(MongoDB.Bson.BsonType.ObjectId)]
public string Id { get; set; }
}
// Derived class
[BsonDiscriminator("Company::Mapping::Geofences::GeoPolygon")]
internal class PolygonGeofenceShape : GeofenceShape
{
}