MongoDB C# Driver 2.0 - Update document - c#

I'm currently upgrading my code to MongoDB C# driver 2.0 and I'm having issues upgrading the code to update documents.
using the old version I was able to do something like this:
MyType myObject; // passed in
var collection = _database.GetCollection<MyType>("myTypes");
var result = collection.Save(myObject);
I'm struggling to find a way to do this in the new version.
I have found a few examples of updating single fields like
var filter = Builders<MyType>.Filter.Eq(s => s.Id, id);
var update = Builders<MyType>.Update.Set(s => s.Description, description);
var result = await collection.UpdateOneAsync(filter, update);
I'd like to update all the fields as I was doing in the old version with the method Save.
Any ideas ?
Thanks a lot

I think you're looking for ReplaceOneAsync():
MyType myObject; // passed in
var filter = Builders<MyType>.Filter.Eq(s => s.Id, id);
var result = await collection.ReplaceOneAsync(filter, myObject)

To add to mnemosyn's answer, while a simple ReplaceOneAsync does update a document it isn't equivalent to Save as Save would also insert the document if it didn't find one to update.
To achieve the same behavior with ReplaceOneAsync you need to use the options parameter:
MyType myObject;
var result = await collection.ReplaceOneAsync(
item => item.Id == id,
myObject,
new UpdateOptions {IsUpsert = true});

you can use LINQ as following:
await context.collection.ReplaceOneAsync(b=> b.Id == item.Id,item);

use ObjectId.Parse(id)
var filter = Builders<MyType>.Filter.Eq(s => s.Id, ObjectId.Parse(id));
var update = Builders<MyType>.Update.Set(s => s.Description, description);
var result = await collection.UpdateOneAsync(filter, update);

Related

MongoDB Sort Query in C# .NET Driver

I've been trying to Sort a collection using MongoDB SortDefinition but whenever I "sort" the collection via a single sort definition, all I get returned is an empty list. However, when I use more than one sort definitions, it returns values.
var TestSort1 = Builders<Scenario>.Sort.Ascending("Name");
var filtered1 = await _context
.DbCollection
.Find(_ => true)
.Sort(TestSort1)
.ToListAsync();
The code above returns an empty list. However, the code below works fine.
var TestSort2 = Builders<Scenario>.Sort.Ascending("Name").Ascending("Owner");
var filtered2 = await _context
.DbCollection
.Find(_ => true)
.Sort(TestSort2)
.ToListAsync();
Is it possible to use a single SortDefinition to sort the collection? Or maybe I am using the SortDefinition wrong?
Maybe you should try using the fluent C# syntax for creating aggregation pipelines...
var collection = database.GetCollection<FiltroCond>("dbCENTRAL");
var filter = Builders<FiltroCond>.Filter.Eq(x => x.PartnerId, cliente)
& Builders<FiltroCond>.Filter.Eq(x => x.TP_PESSOA, 3)
& Builders<FiltroCond>.Filter.Gte(x => x.FG_ATIVO, true);
var result = collection.Aggregate().Match(filter)
.Project(p => new FiltroCond { CD_CLIENTE = p.CD_CLIENTE, ID_CENTRAL = p.ID_CENTRAL, FANTASIA = p.FANTASIA })
.SortBy(p => p.ID_CENTRAL).ToList();
It works fine to me.

How to update document in mongodb using C# driver [duplicate]

I'm currently upgrading my code to MongoDB C# driver 2.0 and I'm having issues upgrading the code to update documents.
using the old version I was able to do something like this:
MyType myObject; // passed in
var collection = _database.GetCollection<MyType>("myTypes");
var result = collection.Save(myObject);
I'm struggling to find a way to do this in the new version.
I have found a few examples of updating single fields like
var filter = Builders<MyType>.Filter.Eq(s => s.Id, id);
var update = Builders<MyType>.Update.Set(s => s.Description, description);
var result = await collection.UpdateOneAsync(filter, update);
I'd like to update all the fields as I was doing in the old version with the method Save.
Any ideas ?
Thanks a lot
I think you're looking for ReplaceOneAsync():
MyType myObject; // passed in
var filter = Builders<MyType>.Filter.Eq(s => s.Id, id);
var result = await collection.ReplaceOneAsync(filter, myObject)
To add to mnemosyn's answer, while a simple ReplaceOneAsync does update a document it isn't equivalent to Save as Save would also insert the document if it didn't find one to update.
To achieve the same behavior with ReplaceOneAsync you need to use the options parameter:
MyType myObject;
var result = await collection.ReplaceOneAsync(
item => item.Id == id,
myObject,
new UpdateOptions {IsUpsert = true});
you can use LINQ as following:
await context.collection.ReplaceOneAsync(b=> b.Id == item.Id,item);
use ObjectId.Parse(id)
var filter = Builders<MyType>.Filter.Eq(s => s.Id, ObjectId.Parse(id));
var update = Builders<MyType>.Update.Set(s => s.Description, description);
var result = await collection.UpdateOneAsync(filter, update);

Linq replace column value with another value

Well, im doing a linq query to get a list of results with the same column, and then i need to replace that column value with a new one.
First Code:
var db = GetContext();
var result = from f in GetContext().ProjectStateHistories
where f.ProjectId.Equals(oldProjectId)
select f;
foreach (var item in result)
{
var projectStateHistoryUpdate = db.ProjectStateHistories.Find(item.Id);
projectStateHistoryUpdate.ProjectId = newProjectId;
db.Entry(projectStateHistoryUpdate).State = EntityState.Modified;
}
db.SaveChanges();
I searched for some answers, and i found that i can use Select, and make a new object (Linq replace null/empty value with another value)
Second Code:
var result = (from f in GetContext().ProjectStateHistories
where f.ProjectId.Equals(oldProjectId)
select f).Select(d=> new { Id = d.Id, EventName = d.EventName, LogUser = d.LogUser, ProjectId = newProjectId, TimeStamp = d.TimeStamp });
And even, Third Code:
var db = GetContext();
var result = (from f in db.ProjectStateHistories
where f.ProjectId.Equals(oldProjectId)
select f).Select(d=> new { ProjectId = newProjectId});
But only the First Code works.
I wanted to ask what i am doing wrong, since i think it is better to change the value with a query, instead of using a foreach.
See code below:
var db = GetContext();
(from f in db.ProjectStateHistories
where f.ProjectId.Equals(oldProjectId)
select f)
.ToList()
.ForEach(i => i.ProjectId = newProjectId);
db.SaveChanges();
Alternatively:
var db = GetContext();
db.ProjectStateHistories
.Where(f => f.ProjectId.Equals(oldProjectId))
.ToList()
.ForEach(f => f.ProjectId = newProjectId);
db.SaveChanges();
The shortest way I know of to replace your code is this:
var db = getcontext();
db.ProjectStateHistories
.Where(f => f.ProjectId.Equals(oldProjectId))
.ToList()
.ForEach(f => f.ProjectId = newProjectId);
db.SaveChanges();
Other answers can be found here
I've just had a thought that could help you, I am just free coding here!
If you just put the for each as part of the select, and then save your changes will that work?
foreach (var source in db.ProjectStateHistories.Where(x => x.ProjectId == oldProjectId))
{
source.ProjectId= newProjectId;
db.Entry(source).State = EntityState.Modified;
}
db.SaveChanges();
I think this is a more efficient way of doing it.
Also the .Select() method is only really useful if you need to Project to a view Model, it won't change the variables in the database, just show them in the newly declared object.
Thanks,
Phill

How to use Contains-filter to expanded table using Odata

I am not able to use Contains-filter to expanded table:
var customersData = await myClient.For<Customer>()
.Filter(t => t.name.Contains(searchCriteria) || t.PersonTable.ContactNumber.Contains(searchCriteria))
.Expand(t => new { t.PersonTable, t.AddressesTable })
.FindEntriesAsync();
I tried using new version 4.12 like below also:
var filter = new ODataExpression<Customer>(t => t.name.Contains(searchCriteria));
filter = filter || new ODataExpression<PersonTable>(x => x.ContactNumber.Contains(searchCriteria));
var result3 = await myClient.For<Customer>().Filter(filter).FindEntriesAsync();
The filter generic type is different from the type you use in For clause, i.e. you query on Customer table and send a filter on PersonTable. The library should probably detect this and throw an exception. Anyway this won't work.

Mongo update array element (.NET driver 2.0)

EDIT:
Not looking for the javascript way of doing this. I am looking for the MongoDB C# 2.0 driver way of doing this (I know it might not be possible; but I hope somebody knows a solution).
I am trying to update the value of an item embedded in an array on the primary document in my mongodb.
I am looking for a strongly typed way to do this. I am using the Mongodb c# 2.0 driver
I can do it by popping the element, updating the value, then reinserting. This just doesn't feel right; since I am overwriting what might have been written in the meantime.
Here is what I have tried so far but with no luck:
private readonly IMongoCollection<TempAgenda> _collection;
void Main()
{
var collectionName = "Agenda";
var client = new MongoClient("mongodb://localhost:27017");
var db = client.GetDatabase("Test");
_collection = db.GetCollection<TempAgenda>(collectionName);
UpdateItemTitle(1, 1, "hello");
}
public void UpdateItemTitle(string agendaId, string itemId, string title){
var filter = Builders<TempAgenda>.Filter.Eq(x => x.AgendaId, agendaId);
var update = Builders<TempAgenda>.Update.Set(x => x.Items.Single(p => p.Id.Equals(itemId)).Title, title);
var result = _collection.UpdateOneAsync(filter, update).Result;
}
Took me a while to figure this out as it doesn't appear to be mentioned in any of the official documentation (or anywhere else). I did however find this on their issue tracker, which explains how to use the positional operator $ with the C# 2.0 driver.
This should do what you want:
public void UpdateItemTitle(string agendaId, string itemId, string title){
var filter = Builders<TempAgenda>.Filter.Where(x => x.AgendaId == agendaId && x.Items.Any(i => i.Id == itemId));
var update = Builders<TempAgenda>.Update.Set(x => x.Items[-1].Title, title);
var result = _collection.UpdateOneAsync(filter, update).Result;
}
Notice that your Item.Single() clause has been changed to Item.Any() and moved to the filter definition.
[-1] or .ElementAt(-1) is apparently treated specially (actually everything < 0) and will be replaced with the positional operator $.
The above will be translated to this query:
db.Agenda.update({ AgendaId: 1, Items.Id: 1 }, { $set: { Items.$.Title: "hello" } })
Thanks, this was helpful. I have an addition though, I've used the above for arrays, pushing to a nested array and pulling from one. The issue I have found is that if I had an int array (So not an object, just a simple int array) that the PullFilter didn't actually work - "Unable to determine the serialization information" which is strange as it's only an array of ints. What I ended up doing was making it an array of objects with only one int parameter, and it all started to work. Possibly a bug, or perhaps my lack of understanding. Anyway, as I've struggled to find information about pulling and pushing to nested object arrays with the C# 2.0 driver, I thought I should post my findings here, as they use the above syntax.
var filter = Builders<MessageDto>.Filter.Where(x => x._id == entity.ParentID && x.NestedArray.Any(i => i._id == entity._id));
var update = Builders<MessageDto>.Update.PullFilter(x => x.NestedArray.ElementAt(-1).User, Builders<User>.Filter.Eq(f => f.UserID, userID));
Collection<MessageDto>(currentUser).UpdateOneAsync(filter, update);
And also:
var filter = Builders<MessageDto>.Filter.Where(x => x._id == entity.ParentID && x.NestedArray.Any(i => i._id == entity._id));
var update = Builders<MessageDto>.Update.Push(x => x.NestedArray.ElementAt(-1).Users, new User { UserID = userID });
Collection<MessageDto>(currentUser).UpdateOneAsync(filter, update);
The correct way to update a Document or sub array is as follows:
var filter = Builders<Declaracion>.Filter.Where(x => x.Id == di && x.RemuneracionMensualActual.RemuneracionActIndustrial.Any(s => s.Id == oid));
var update = Builders<Declaracion>.Update.Set(x => x.RemuneracionMensualActual.RemuneracionActIndustrial.ElementAt(-1).Ingreso, datos.ActividadIndustrial.Ingreso)
.Set(x => x.RemuneracionMensualActual.RemuneracionActIndustrial.ElementAt(-1).RazonSocial, datos.ActividadIndustrial.RazonSocial)
.Set(x => x.RemuneracionMensualActual.RemuneracionActIndustrial.ElementAt(-1).TipoNegocio, datos.ActividadIndustrial.TipoNegocio);

Categories