Reputation: 1121
On calling UpdateOneAsync, with this wrapper:
public async Task<UpdateResult> UpdateDocument<T>(
string sCollectionName,
Expression<Func<T, bool>> filter,
UpdateDefinition<T> update,
bool bUpsert,
System.Threading.CancellationToken cancellationToken
)
{
IMongoDatabase db = _mongoClient.GetDatabase(_optionsMonitor.CurrentValue.databasename);
IMongoCollection<T> collection = db.GetCollection<T>(sCollectionName);
return await collection.UpdateOneAsync<T>(filter, update, new UpdateOptions() { IsUpsert = bUpsert }, cancellationToken);
}
Like so:
private async Task<Models.Errors> UpdateDbOnSyncServerToBoardUpdate(
CancellationToken cancel,
MongoDB.Bson.BsonDocument bsonDocConfigurationToUpdate,
DateTime dtUpdated,
string sId,
int iObjectId,
string sAppName,
string sModelName
)
{
MongoDB.Driver.UpdateResult updateResult = null;
Models.Errors errors = null;
try
{
updateResult = await _db.UpdateDocument<Models.Database.NodeBoardModel>(
Constants.NodeBoardCollectionName,
node => node.Id == sId &&
node.RemoteBoard.apps.SingleOrDefault(
app => app.appname == sAppName).objects.
SingleOrDefault(model => model.name == sModelName).config_docs.
Any(config => config.config_id == iObjectId),
MongoDB.Driver.Builders<Models.Database.NodeBoardModel>.Update.
Set(
node => node.RemoteBoard.apps[-1].objects[-1].config_docs[-1].config_doc, bsonDocConfigurationToUpdate).
Set(
node => node.RemoteBoard.apps[-1].objects[-1].config_docs[-1].config_dt, dtUpdated),
false,
cancel
);
I receive a NotSupportedException:
The expression tree is not supported: {document}{RemoteBoard}{apps}.SingleOrDefault(app => (app.appname == "eACM")).objects.SingleOrDefault(model => (model.name == "tag")).config_docs
I sense I am using a LINQ keyword the wrong way or in a way that's not supported by MongoDb but it's hard to tell exactly where the problem lies.
I can't make anything of the stack trace:
at MongoDB.Driver.Linq.Processors.EmbeddedPipeline.EmbeddedPipelineBinder.BindNonMethodCall(Expression node) at MongoDB.Driver.Linq.Processors.PipelineBinderBase
1.BindPipeline(Expression node) at MongoDB.Driver.Linq.Processors.PipelineBinderBase
1.BindMethodCall(MethodCallExpression node) at MongoDB.Driver.Linq.Processors.EmbeddedPipeline.EmbeddedPipelineBinder.Bind(Expression node, IBindingContext parent) at MongoDB.Driver.Linq.Processors.SerializationBinder.VisitMethodCall(MethodCallExpression node) at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor) at MongoDB.Driver.Linq.Processors.SerializationBinder.Visit(Expression node) at System.Linq.Expressions.ExpressionVisitor.VisitBinary(BinaryExpression node) at MongoDB.Driver.Linq.Processors.SerializationBinder.VisitBinary(BinaryExpression node) at System.Linq.Expressions.BinaryExpression.Accept(ExpressionVisitor visitor) at MongoDB.Driver.Linq.Processors.SerializationBinder.Visit(Expression node) at MongoDB.Driver.Linq.Translators.PredicateTranslator.Translate[TDocument](Expression1 predicate, IBsonSerializer
1 parameterSerializer, IBsonSerializerRegistry serializerRegistry) at MongoDB.Driver.MongoCollectionImpl1.ConvertWriteModelToWriteRequest(WriteModel
1 model, Int32 index) at System.Linq.Enumerable.SelectIterator[TSource,TResult](IEnumerable1 source, Func
3 selector)+MoveNext() at System.Collections.Generic.List1.AddEnumerable(IEnumerable
1 enumerable) at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source) at MongoDB.Driver.Core.Operations.BulkMixedWriteOperation..ctor(CollectionNamespace collectionNamespace, IEnumerable
1 requests, MessageEncoderSettings messageEncoderSettings) at MongoDB.Driver.MongoCollectionImpl1.CreateBulkWriteOperation(IEnumerable
1 requests, BulkWriteOptions options) at MongoDB.Driver.MongoCollectionImpl1.BulkWriteAsync(IClientSessionHandle session, IEnumerable
1 requests, BulkWriteOptions options, CancellationToken cancellationToken) at MongoDB.Driver.MongoCollectionImpl1.UsingImplicitSessionAsync[TResult](Func
2 funcAsync, CancellationToken cancellationToken) at MongoDB.Driver.MongoCollectionBase1.UpdateOneAsync(FilterDefinition
1 filter, UpdateDefinition1 update, UpdateOptions options, Func
3 bulkWriteAsync) at WebApplication.Services.ConcreteDatabase.UpdateDocument[T](String sCollectionName, Expression1 filter, UpdateDefinition
1 update, Boolean bUpsert, CancellationToken cancellationToken) in C:\GIT\app-manager\APIMM\ServerLevelConfiguration\WebApplication\Services\ConcreteDatabase.cs:line 131 at WebApplication.Services.SyncBoardDatabaseBackgroundService.UpdateDbOnSyncServerToBoardUpdate(CancellationToken cancel, BsonDocument bsonDocConfigurationToUpdate, DateTime dtUpdated, String sId, Int32 iObjectId, String sAppName, String sModelName) in C:\GIT\app-manager\APIMM\ServerLevelConfiguration\WebApplication\Services\SyncBoardDatabaseBackgroundService.cs:line 353
Model classes:
public class NodeBoardModel
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[BsonElement]
public NodeBoardRemoteModel RemoteBoard { get; set; }
}
public class NodeBoardRemoteModel
{
[BsonElement]
public List<NodeBoardAppModel> apps { get; set; }
}
public class NodeBoardAppModel
{
[BsonElement]
public string appname { get; set; }
[BsonElement]
public List<NodeBoardObjectModel> objects { get; set; }
}
public class NodeBoardObjectModel
{
[BsonElement]
public string name { get; set; }
[BsonElement]
public List<NodeBoardObjectConfigurationModel> config_docs { get; set; }
}
public class NodeBoardObjectConfigurationModel
{
[BsonElement]
public BsonDocument config_doc { get; set; }
[BsonElement]
public DateTime config_dt { get; set; }
[BsonElement]
public int config_id { get; set; }
}
Upvotes: 1
Views: 3996
Reputation: 49985
The problem starts when you're trying to build your Update
statement. As you probably know -1
passed as an index will be translated to the $ positional operator. The documentation says that
The positional $ operator cannot be used for queries which traverse more than one array, such as queries that traverse arrays nested within other arrays, because the replacement for the $ placeholder is a single value
Additionally you're trying to build your filtering condition using SingleOrDefault
and .NET MongoDB driver is not able to translate that into any MongoDB query syntax operator.
How to fix that ?
Instead of using the positional operator you can try using the positional filtered operator syntax.
var filter = Builders<NodeBoardModel>.Filter.Eq(f => f.Id, sId);
var update = Builders<NodeBoardModel>.Update.Set("RemoteBoard.apps.$[app].objects.$[object].config_docs.$[configdoc].config_dt", dtUpdated);
var arrayFilters = new List<ArrayFilterDefinition>();
ArrayFilterDefinition<BsonDocument> appFilter = new BsonDocument("app.appname", new BsonDocument("$eq", sAppName));
ArrayFilterDefinition<BsonDocument> objectFilter = new BsonDocument("object.name", new BsonDocument("$eq", sModelName));
ArrayFilterDefinition<BsonDocument> configDocFilter = new BsonDocument("configdoc.config_id", new BsonDocument("$eq", iObjectId));
arrayFilters.AddRange(new[] { appFilter, objectFilter, configDocFilter });
var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };
var res = Col.UpdateOne(filter, update, updateOptions);
Upvotes: 4