How to retrieve all _ids of updated documents in MongoDB C#? - c#

I want to run multiple upsert queries using bulkWrite(). But the problem is, bulkWrite only returns inserted _ids not updated _ids. How can I get all
_ids in this query?
var insertCommand = Builders<myObject>.Update.SetOnInsert(d => d.Name, object.Name).SetOnInsert(d => d.Age, object.Age);
var updateCommand = Builders<myObject>.Update.Set(d => d.LastRequest, object.DateTime);
var updateQueries = Builders<myObject>.Update.Combine(new[] { updateCommand, insertCommand });
var filter = Builders<myObject>.Filter;
var findQuery = filter.And(filter.Eq(e => e.Name, object.Name), filter.Eq(e => e.City, object.City));
var upsertObject = new UpdateOneModel<myObject>(findQuery, updateQueries) { IsUpsert = true };
bulkOps.Add(upsertObject);
var result = await Collection.BulkWriteAsync(bulkOps);

Using BulkWriteAsync, I don't think this can be done. The documentation on the MongoDB bulkWrite operation states the following:
Returns:
A boolean acknowledged as true if the operation ran with
write concern or false if write concern was disabled.
A count for each write operation.
An array containing an _id for each successfully
inserted or upserted documents.
What works, however, for a single instance is this:
var result = collection.FindOneAndUpdate(findQuery, updateQueries, new FindOneAndUpdateOptions<myObject> { IsUpsert = true, ReturnDocument = ReturnDocument.After});
After running this, result will hold the modified document including its _id.

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

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.

MongoDB C# Driver 2.0 - Update document

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 select from CRM public view

I have searching for a while now, but couldn't find how to query from public view. For example, I have predefined public view called Active Accounts and I want data from it.
So far I only know this way, but that not include any views:
using (var xrm = new XrmServiceContext("Xrm"))
{
var activeAccounts = from a in xrm.AccountSet
where a.StateCode == 0
select new { a.Id, a.Name };
// TODO ...
}
But I would like to do it like this (not working, ActiveAccountsView not exist, it's pseudo):
using (var xrm = new XrmServiceContext("Xrm"))
{
var activeAccounts = from a in xrm.ActiveAccountsView
select new { a.Id, a.Name };
// TODO ...
}
Is this even possible?
The query definitions of public views are stored in the savedquery entity, that can be queried using common techniques.
Out-of-the-box views are stored with a fixed ID, so querying Active Accounts on the OrganizationServiceContext object could be done in the following way:
private static IEnumerable<Entity> GetActiveAccounts(OrganizationServiceContext serviceContext)
{
string fetchXml = serviceContext
.CreateQuery("savedquery")
.Where(sq =>
sq.GetAttributeValue<Guid>("savedqueryid") == new Guid("00000000-0000-0000-00AA-000010001002"))
.Select(sq => sq.GetAttributeValue<string>("fetchxml"))
.First();
var request = new RetrieveMultipleRequest
{
Query = new FetchExpression(fetchXml)
};
var response = (RetrieveMultipleResponse) serviceContext.Execute(request);
return response.EntityCollection.Entities;
}
It is not possible to use LINQ here. LINQ relies on the QueryExpression class, but does not implement all its capabilities (OUTER JOIN is a painful omission for example). So, while it is possible to convert a LINQ query to a QueryExpression, the other way around is not.
Paging can be applied by editing the Fetch XML string, but if that is too much hassle, you can also consider to convert the Fetch XML to a QueryExpression and apply paging on that object:
private IEnumerable<Entity> GetActiveAccounts(int pageNumber)
{
string fetchXml = _serviceContext
.CreateQuery("savedquery")
.Where(sq =>
sq.GetAttributeValue<Guid>("savedqueryid") == new Guid("00000000-0000-0000-00AA-000010001002"))
.Select(sq => sq.GetAttributeValue<string>("fetchxml"))
.First();
var conversionRequest = new FetchXmlToQueryExpressionRequest
{
FetchXml = fetchXml
};
var response = (FetchXmlToQueryExpressionResponse)_serviceContext.Execute(conversionRequest);
response.Query.PageInfo = new PagingInfo { Count = 1, PageNumber = pageNumber };
var queryRequest = new RetrieveMultipleRequest
{
Query = response.Query
};
var result = (RetrieveMultipleResponse) _serviceContext.Execute(queryRequest);
return result.EntityCollection.Entities;
}
Additional advantage of the QueryExpression vs. Fetch XML is that it is processed in a bit more efficient way.
The very same can be done with user defined views; these views are stored in the userquery entity. The only difference here is you cannot rely on a fixed view ID. Instead you would need to filter your query on querytype, name, returnedtypecode, ownerid and/or other criteria.
Dynamics CRM also has an OrganizationRequest that allows you to execute the savedquery immediately. However, it returns its result as a resultset XML string, so you would still need to deserialize the response. (A nice example can be found here.) Also, I am not sure if it is possible to limit the result set to a specific page when using the ExecuteByIdSavedQueryRequest:
var request = new ExecuteByIdSavedQueryRequest
{
EntityId = new Guid("00000000-0000-0000-00AA-000010001002")
};
var response = (ExecuteByIdSavedQueryResponse)serviceContext.Execute(request);
string resultset = response.String;

Categories