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")
);