使用MongoDB C#Client在FindAndModify中调用db.eval

时间:2015-01-22 10:33:50

标签: mongodb mongodb-.net-driver

我有以下文件:

{
"_id": 100,
"Version": 1,
"Data": "Hello"
}

我有一个从序列中返回数字的函数:

function getNextSequence(name) {
   var ret = db.Counter.findAndModify(
          {
            query: { _id: name },
            update: { $inc: { value: 1 } },
            new: true,
            upsert: true
          }
   );

   return ret.value;
}

我可以通过执行以下Mongo命令将其用于乐观并发:

db.CollectionName.findAndModify({
    query: { "_id" : NumberLong(100), "Version" : 1 },
    update: { "$set" : { 
         "Data": "Here is new data!",
         "Version" : db.eval('getNextSequence("CollectionName")') } 
    },
    new: true
    }
);

这将更新文档(作为_id和Version)匹配,使用新的数据字段,以及eval调用之外的新数字。

它还会返回一个修改过的文档,如果我想稍后再次进行更新(在相同的会话中),我可以从中检索新的版本值。

我的问题是:

您无法使用将序列化为此命令的MongoDB C#客户端创建更新文档。

我用过:

 var update = Update.Combine(
                new UpdateDocument("$set", doc),
                Update.Set(versionMap.ElementName, new BsonJavaScript("db.eval('getNextSequence(\"Version:CollectionName\")')")))
                );

如果您使用我最初期望执行此任务的内容,BsonJavascript,您将获得以下文档,该文档错误地将Version设置为javascript字符串。

update: { "$set" : { 
         "Data": "Here is new data!",
         "Version" : { "$code" : "db.eval('getNextSequence(\"Version:CollectionName\")')" }
           }
 }

如何让MongoDB C#客户端使用我的db.eval函数调用来序列化更新文档?

我试图在我的程序集中添加一个新的BsonValue类型,我将序列化为db.eval('');但是有一个我无法修改的BsonType枚举,没有为MongoDB创建一个mod,我不想做任何改变,兼容性等方面的问题。

我也尝试过自己创建一个更新文档作为BsonDocument,但FindAndModify只接受一个I​​MongoUpdate接口,这个接口只是一个标记,目前我发现它是多余的。

我刚刚尝试通过自己创建一个BsonDocument手动构造命令来设置Value:db.eval,但是我得到以下异常: String值无法写入BSON文档的根级别。

我现在看不到别的办法,只需要下载到Mongo流级别来完成此任务。

1 个答案:

答案 0 :(得分:2)

所以我放弃了尝试让Mongo C#Client做我需要的东西,而是写了下面的MongoDB函数来为我做这个:

db.system.js.save(
   {
     _id : "optimisticFindAndModify" ,
     value : function optimisticFindAndModify(collectionName, operationArgs) {
           var collection = db.getCollection(collectionName);
           var ret = collection.findAndModify(operationArgs);
           return ret;
        }
   }
);

这将使集合运行,并在FindAndModify操作中执行传递的operationArgs。

因为我无法让shell在javascript对象上设置文字值(即,不是"引用的字符串"),所以我不得不在我的C#代码中使用它:

var counterName = "Version:" + CollectionName;
var sequenceJs = string.Format("getNextSequence(\"{0}\")", counterName);

var doc = entity.ToBsonDocument();
doc.Remove("_id");
doc.Remove(versionMap.ElementName);
doc.Add(versionMap.ElementName, "SEQUENCEJS");

var findAndModifyDocument = new BsonDocument
{
    {"query", query.ToBsonDocument()},
    {"update", doc},
    {"new", true},
    {"fields", Fields.Include(versionMap.ElementName).ToBsonDocument() }
};

// We have to strip the quotes from getNextSequence.
var findAndModifyArgs = findAndModifyDocument.ToString();
findAndModifyArgs = findAndModifyArgs.Replace("\"SEQUENCEJS\"", sequenceJs);

var evalCommand = string.Format("db.eval('optimisticFindAndModify(\"{0}\", {1})');", CollectionName, findAndModifyArgs);
var modifiedDocument = Database.Eval(new EvalArgs
{
    Code = new BsonJavaScript(evalCommand)
});

这样做的结果是我现在可以在optimisticFindAndModify函数中调用我的Sequence Javascript,即getNextSequence函数。

不幸的是我不得不在C#中使用字符串替换,因为没有办法设置BsonDocument来使用必要的文字类型db.eval,尽管Mongo Shell很喜欢它。

现在一切正常。

编辑:

虽然,如果你真的想要突破边界,并且实际上是清醒的,你会意识到同样的动作可以通过在Version字段上执行$ inc来实现....并且这些都不是必需的....

但是:如果你想继续关注如何实现并发的MongoDB教程,或者你只是想在FindAndModify中使用一个函数,这将对你有所帮助。我知道我可能会在这个项目中多次提及它!