C# C“驱动程序MongoDb:如何使用”;UpdateOneAsync“;

C# C“驱动程序MongoDb:如何使用”;UpdateOneAsync“;,c#,mongodb-.net-driver,C#,Mongodb .net Driver,我只想更新文档的单个字段。我的意思是要理解我必须使用UpdateOneAsync。当我尝试这样做时,我总是得到MongoDB.Bson.BsonSerializationException:元素名“Test”无效。 下面的代码再现了我的问题(Docker中的xUnit、.NET内核、MongoDb) 公共类设备 { 公共类DummyDocument:IDocument { 公共字符串测试{get;set;} 公共Guid Id{get;set;} 公共int版本{get;set;} } [事实]

我只想更新文档的单个字段。我的意思是要理解我必须使用
UpdateOneAsync
。当我尝试这样做时,我总是得到
MongoDB.Bson.BsonSerializationException:元素名“Test”无效。

下面的代码再现了我的问题(Docker中的xUnit、.NET内核、MongoDb)

公共类设备
{
公共类DummyDocument:IDocument
{
公共字符串测试{get;set;}
公共Guid Id{get;set;}
公共int版本{get;set;}
}
[事实]
公共异步任务复制()
{
var db=新的MongoDbContext(“mongodb://localhost:27017“,“myDb”);
var document=new DummyDocument{Test=“abc”,Id=Guid.Parse(“69695d2c-90e7-4a4c-b478-6c8fb2a1dc5c”);
等待db.GetCollection().InsertOneAsync(文档);
FilterDefinition u=新表达式FilterDefinition(d=>d.Id==document.Id);
wait db.GetCollection().UpdateOneAsync(u,newobjectupdatedefinition(new-DummyDocument{Test=“bla”}));
}
}

您是否尝试过按中所示的方式进行操作?我想对你来说可能是这样的:

[事实]
公共异步任务复制()
{
var db=新的MongoDbContext(“mongodb://localhost:27017“,“myDb”);
var document=new DummyDocument{Test=“abc”,Id=Guid.Parse(“69695d2c-90e7-4a4c-b478-6c8fb2a1dc5c”);
等待db.GetCollection().InsertOneAsync(文档);
var filter=Builders.filter.Eq(“Id”,document.Id);
var update=Builders.update.Set(“Test”、“bla”);
wait db.GetCollection().UpdateOneAsync(过滤器,更新);
}
更新:

connectionString = "mongodb://localhost:27017";
client = new MongoClient(connectionString);
database = client.GetDatabase("authors_and_books");      // rename as appropriate
collection = database.GetCollection<Author>("authors");  // rename as appropriate

public class Author // Parent class
{
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string LifeExpectancy { get; set; }
    public IList<Book> Books { get; set; }
    = new List<Book>();
}

public class Book // Child class
{
    public Guid Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public Guid ParentId { get; set; }
}
根据OP comment,要发送整个对象而不是更新特定属性,请尝试:

⋮
var filter=Builders.filter.Eq(“Id”,document.Id);
wait db.GetCollection().ReplaceOneAsync(过滤器,新的DummyDocument{Test=“bla”},新的更新选项{IsUpsert=true});

您是否尝试过按中所示的方式进行操作?我想对你来说可能是这样的:

[事实]
公共异步任务复制()
{
var db=新的MongoDbContext(“mongodb://localhost:27017“,“myDb”);
var document=new DummyDocument{Test=“abc”,Id=Guid.Parse(“69695d2c-90e7-4a4c-b478-6c8fb2a1dc5c”);
等待db.GetCollection().InsertOneAsync(文档);
var filter=Builders.filter.Eq(“Id”,document.Id);
var update=Builders.update.Set(“Test”、“bla”);
wait db.GetCollection().UpdateOneAsync(过滤器,更新);
}
更新:

connectionString = "mongodb://localhost:27017";
client = new MongoClient(connectionString);
database = client.GetDatabase("authors_and_books");      // rename as appropriate
collection = database.GetCollection<Author>("authors");  // rename as appropriate

public class Author // Parent class
{
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string LifeExpectancy { get; set; }
    public IList<Book> Books { get; set; }
    = new List<Book>();
}

public class Book // Child class
{
    public Guid Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public Guid ParentId { get; set; }
}
根据OP comment,要发送整个对象而不是更新特定属性,请尝试:

⋮
var filter=Builders.filter.Eq(“Id”,document.Id);
wait db.GetCollection().ReplaceOneAsync(过滤器,新的DummyDocument{Test=“bla”},新的更新选项{IsUpsert=true});

最近,为了学习的目的,我尝试在自己的项目中实施
UpdateOneAsync
方法,并且相信我获得的见解可能对
msallin
和其他人有所帮助。我还介绍了
UpdateManyAsync
的用法

msallin
的帖子中,我得到的印象是,所需的功能涉及到更新给定对象类的一个或多个字段。
crgolden
的注释(以及其中的注释)证实了这一点,表示不需要替换整个文档(在数据库中),而是需要替换与存储在数据库中的对象类相关的字段。还需要了解
ObjectUpdateDefinition
的用法,我不熟悉,也不直接使用它。因此,我将展示如何实现前一个功能

为了涵盖
msallin
的潜在用例,我将展示如何更新父类和子类上的字段。在写这篇文章时,所展示的每个例子都已经运行过了,并被发现是有效的

连接信息(使用MongoDB.Driver v2.9.2),以及父类和子类结构:

connectionString = "mongodb://localhost:27017";
client = new MongoClient(connectionString);
database = client.GetDatabase("authors_and_books");      // rename as appropriate
collection = database.GetCollection<Author>("authors");  // rename as appropriate

public class Author // Parent class
{
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string LifeExpectancy { get; set; }
    public IList<Book> Books { get; set; }
    = new List<Book>();
}

public class Book // Child class
{
    public Guid Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public Guid ParentId { get; set; }
}
测试数据:要添加到现有作者的子图书类
有些书与其他书具有相同的
ParentId
。这是故意的

var bookEntities = new List<Book>()
{
    new Book()
    {
        Id = new Guid("2ee90507-8952-481e-8866-4968cdd87e74"),
        Title = "My First Book",
        Description = "A book that makes you fall asleep and..",
        ParentId = new Guid("6f51ea64-6d46-4eb2-b5d5-c92cdaf3260c")
    },
    new Book()
    {
        Id = new Guid("da89edbd-e8cd-4fde-ab73-4ef648041697"),
        Title = "Book about trees",
        Description = "Forthcoming 100th novel in Some Thing.",
        ParentId = new Guid("412c3012-d891-4f5e-9613-ff7aa63e6bb3")
    },
    new Book()
    {
        Id = new Guid("db76a03d-6503-4750-84d0-9efd64d9a60b"),
        Title = "Book about tree saplings",
        Description = "Impressive 101th novel in Some Thing",
        ParentId = new Guid("412c3012-d891-4f5e-9613-ff7aa63e6bb3")
    },
    new Book()
    {
        Id = new Guid("ee2d2593-5b22-453b-af2f-bcd377dd75b2"),
        Title = "The Winds of the Wind",
        Description = "Try not to wind along..",
        ParentId = new Guid("25320c5e-f58a-4b1f-b63a-8ee07a840bdf")
    }
};
迭代分组的书籍实体
我们现在使用
c
,而不是
book
,来表示
ForEachAsync
迭代中的书籍。通过公共
ParentId
将书籍分组为匿名对象,可以防止重复调用同一父对象的
UpdateOneAsync
,从而有助于减少开销。
AddToSetEach
方法允许我们将一本或多本书附加到作者的图书集合中,只要这些书是作为
IEnumerable
输入的

使用C#LINQ简化 注意,我们可以使用
C#LINQ
简化上面的一些语句,特别是关于
UpdateOneAsync(filter,update)
方法中使用的过滤器。下面的例子

var authors = bookEntities.GroupBy(c => c.ParentId, c => c,
    (key, books) => new Author() { Id = key, Books = books.ToList() });
await authors.ToAsyncEnumerable().ForEachAsync(async c => await collection.UpdateOneAsync(
    p => p.Id == c.Id,
    Builders<Author>.Update.AddToSetEach(z => z.Books, c.Books)
    ));
使用UpdateManayAsync
由于在运行
ForEachAsync
迭代之前,已知任何给定作者的命运,因此我们可以专门查询,并且只更改那些匹配项(而不必调用
Set
,来更改我们不想更改的匹配项)。如果需要,可以从上述迭代中排除此集合操作,并使用
updatemanayasync
单独使用。我们使用与上述声明相同的
authors
变量。由于不使用和使用
C#LINQ
之间存在较大差异,我在这里展示了两种方法(选择一种)。我们一次使用多个过滤器,即作为
构建器。在C#中,它是通过键入
[-1]
来声明的,这相当于Mongo DB Shell中的
$
。在撰写本文时,驱动程序似乎只支持将
[-1]
一起使用
var authors = bookEntities.GroupBy(c => c.ParentId, c => c,
    (key, books) => new Author() { Id = key, Books = books.ToList() });
await authors.ToAsyncEnumerable().ForEachAsync(async c => await collection.UpdateOneAsync(
    Builders<Author>.Filter.Eq(p => p.Id, c.Id),
    Builders<Author>.Update.AddToSetEach(z => z.Books, c.Books)
    ));
var authors = bookEntities.GroupBy(c => c.ParentId, c => c,
    (key, books) => new Author() { Id = key, Books = books.ToList() });
await authors.ToAsyncEnumerable().ForEachAsync(async c => await collection.UpdateOneAsync(
    p => p.Id == c.Id,
    Builders<Author>.Update.AddToSetEach(z => z.Books, c.Books)
    ));
var authors = bookEntities.GroupBy(c => c.ParentId, c => c,
    (key, books) => new DbAuthor()
    {
        Id = key,
        Books = books.ToList(),
        LastName = collection.Find(c => c.Id == key).FirstOrDefault().LastName,
        LifeExpectancy = collection.Find(c => c.Id == key).FirstOrDefault().LifeExpectancy
    });

await authors.ToAsyncEnumerable().ForEachAsync(async c => await collection.UpdateOneAsync(
    p => p.Id == c.Id,
    Builders<DbAuthor>.Update.Combine(
        Builders<Author>.Update.AddToSetEach(z => z.Books, c.Books),
        Builders<Author>.Update.Inc(z => z.Version, 0.01),
        Builders<Author>.Update.Set(z => z.LifeExpectancy,
            (c.LastName == "King") ? "Will live forever" : c.LifeExpectancy)
        )));
var authorIds = authors.Select(c => c.Id).ToList();

// Using builders:
await collection.UpdateManyAsync(
    Builders<Author>.Filter.And(
        Builders<Author>.Filter.In(c => c.Id, authorIds),
        Builders<Author>.Filter.Eq(p => p.LastName, "King")
        ),
    Builders<Author>.Update.Set(p => p.LifeExpectancy, "Will live forever"));

// Using C# LINQ:    
await collection.UpdateManyAsync<Author>(c => authorIds.Contains(c.Id) && c.LastName == "King",
    Builders<Author>.Update.Set(p => p.LifeExpectancy, "Will live forever"));
// Shouldn't all authors with lastname "King" live eternally?
await collection.UpdateManyAsync<Author>(c => c.LastName == "King",
    Builders<Author>.Update.Set(p => p.LifeExpectancy, "Will live forever"));

// Given a known author id, how do I update the related author?
var authorId = new Guid("412c3012-d891-4f5e-9613-ff7aa63e6bb3");
await collection.UpdateManyAsync<Author>(c => c.Id == authorId), 
    Builders<Author>.Update.Set(p => p.LifeExpectancy, "Will not live forever"));

// Given a known book id, how do I update any related authors?
var bookId = new Guid("c7ba6add-09c4-45f8-8dd0-eaca221e5d93");
await collection.UpdateManyAsync<Author>(c => c.Books.Any(p => p.Id == bookId),
    Builders<Author>.Update.Set(p => p.LifeExpectancy, "Death by doorsteps in due time"));
// Given a known book id, how do I update only that book? (non-async, also works with UpdateOne)
var bookId = new Guid("447eb762-95e9-4c31-95e1-b20053fbe215");
collection.FindOneAndUpdate(
    Builders<Author>.Filter.ElemMatch(c => c.Books, p => p.Id == bookId),
    Builders<Author>.Update.Set(z => z.Books[-1].Title, "amazing new title")
    );

// Given a known book id, how do I update only that book? (async)
var bookId = new Guid("447eb762-95e9-4c31-95e1-b20053fbe215");
await collection.UpdateOneAsync(
    Builders<Author>.Filter.ElemMatch(c => c.Books, p => p.Id == bookId),
    Builders<Author>.Update.Set(z => z.Books[-1].Title, "here we go again")
    );

// Given several known book ids, how do we update each of the books?
var bookIds = new List<Guid>()
{
    new Guid("c7ba6add-09c4-45f8-8dd0-eaca221e5d93"),
    new Guid("bc4c35c3-3857-4250-9449-155fcf5109ec"),
    new Guid("447eb762-95e9-4c31-95e1-b20053fbe215")
};       
await bookIds.ToAsyncEnumerable().ForEachAsync(async c => await collection.UpdateOneAsync(
    p => p.Books.Any(z => z.Id == c),
    Builders<Author>.Update.Set(z => z.Books[-1].Title, "new title yup yup")
    ));
await collection.UpdateManyAsync(
    c => c.Books.Any(p => bookIds.Contains(p.Id)),
    Builders<Author>.Update.Set(z => z.Books[-1].Title, "new title once per author")
    );