我正在编写C#代码,该代码将写入现有Web应用程序(用PHP编写)所使用的Mongo数据库中,因此无需更改数据库的现有结构。数据库结构如下所示:
{
"_id": ObjectId("5572ee670e86b8ec0ed82c61")
"name": "John Q. Example",
"guid": "12345678-1234-5678-abcd-fedcba654321",
"recordIsDeleted": false,
"address":
{
"line1": "123 Main St.",
"city": "Exampleville"
}
}
我将其读入如下所示的类中:
public class Person : MongoMappedBase
{
public ObjectId Id { get; set; }
public Guid Guid { get; set; }
public bool RecordIsDeleted { get; set; }
public string Name { get; set; }
public AddressData Address { get; set; }
// etc.
}
public class AddressData : MongoMappedBase
{
public string Line1 { get; set; }
public string City { get; set; }
// etc.
}
阅读代码如下所示:
var collection = db.GetCollection<Person>("people");
List<Person> people = collection.Find<Person>(_ => true).ToListAsync().Result;
(注意:我仍在开发中。在生产中,我将一次切换到ToCursorAsync()
并遍历数据,因此不必担心我会将整个列表拉入内存的事实。)
到现在为止还挺好。
但是,当我写出数据时,它是这样的:
{
"_id": ObjectId("5572ee670e86b8ec0ed82c61")
"name": "John Q. Example",
"guid": "12345678-1234-5678-abcd-fedcba654321",
"recordIsDeleted": false,
"address":
{
"_t": "MyApp.MyNamespace.AddressData, MyApp",
"_v":
{
"line1": "123 Main St.",
"city": "Exampleville"
}
}
}
注意该address
字段看起来如何。那不是我想要的 我希望地址数据看起来像地址数据输入(否_t
或_v
字段)。换句话说,最后作为内容的部分_v
是我想要作为address
字段值保留在Mongo数据库中的内容。
现在,如果我只是从自己的C#代码中使用Mongo数据库,那可能很好:如果我要反序列化此数据结构,则我假设(尽管我尚未验证)Mongo将使用_t
and_v
字段来创建正确类型(AddressData
)的实例,并将其放在实例的Address
属性中Person
。在这种情况下,一切都会好起来的。
但是,我与一个PHP Web应用程序共享了该数据库,该Web应用程序不希望在地址数据中看到这些_t
和_v
值,并且不知道如何处理它们。我要告诉蒙戈“请不要不序列化的类型Address
属性。姑且认为它始终将是一个AddressData
实例,只是序列化的内容没有任何鉴别。”
我当前用于将对象持久保存到Mongo的代码如下所示:
public UpdateDefinition<TDocument> BuildUpdate<TDocument>(TDocument doc) {
var builder = Builders<TDocument>.Update;
UpdateDefinition<TDocument> update = null;
foreach (PropertyInfo prop in typeof(TDocument).GetProperties())
{
if (prop.PropertyType == typeof(MongoDB.Bson.ObjectId))
continue; // Mongo doesn't allow changing Mongo IDs
if (prop.GetValue(doc) == null)
continue; // If we didn't set a value, don't change existing one
if (update == null)
update = builder.Set(prop.Name, prop.GetValue(doc));
else
update = update.Set(prop.Name, prop.GetValue(doc));
}
return update;
}
public void WritePerson(Person person) {
var update = BuildUpdate<Person>(person);
var filter = Builders<Person>.Filter.Eq(
"guid", person.Guid.ToString()
);
var collection = db.GetCollection<Person>("people");
var updateResult = collection.FindOneAndUpdateAsync(
filter, update
).Result;
}
在那儿的某个地方,我需要告诉Mongo:“我不在乎_t
该Address
属性上的字段,我什至都不希望看到它。我知道我在该字段中坚持使用哪种类型的对象,它们永远一样。” 但是我还没有在Mongo文档中找到任何可以告诉我如何做的东西。有什么建议么?
我想到了。我确实确实遇到了https://groups.google.com/forum/#!topic/mongodb-user/QGctV4Hbipk中描述的问题,其中Mongo希望使用基本类型,但要使用派生类型。鉴于上面的代码,Mongo期望的基本类型实际上是object
!我发现这builder.Set()
实际上是一个通用方法,builder.Set<TField>
可以TField
从第二个参数(字段数据)的类型中找出其类型参数。由于我正在使用prop.GetValue()
,并且返回object
,因此Mongoobject
在我的Address
字段(以及我遗漏的其他字段)中期望有一个实例,因此将_t
所有这些字段放进去。
答案是显式转换从中返回的对象prop.GetValue()
,以便在这种情况下builder.Set()
可以调用正确的泛型方法(builder.Set<AddressData>()
而不是builder.Set<object>()
)。以下内容有些丑陋(我希望有一种方法可以在运行时通过反射来获取特定的泛型函数重载,因为我可以将整个switch
语句转换为基于反射的单个方法调用),但它确实有效:
public UpdateDefinition<TDocument> BuildUpdate<TDocument>(TDocument doc) {
var builder = Builders<TDocument>.Update;
var updates = new List<UpdateDefinition<TDocument>>();
foreach (PropertyInfo prop in typeof(TDocument).GetProperties())
{
if (prop.PropertyType == typeof(MongoDB.Bson.ObjectId))
continue; // Mongo doesn't allow changing Mongo IDs
if (prop.GetValue(doc) == null)
continue; // If we didn't set a value, don't change existing one
switch (prop.PropertyType.Name) {
case "AddressData":
updates.add(builder.Set(prop.Name, (AddressData)prop.GetValue(doc)));
break;
// Etc., etc. Many other type names here
default:
updates.add(builder.Set(prop.Name, prop.GetValue(doc)));
break;
}
}
return builder.Combine(updates);
}
这导致了Address
现场,并正在坚持没有任何所有我在我真正的代码遇到问题,其他领域_t
或_v
领域,就像我想要的。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句