diff --git a/CoreIntegrationTests/Infrastructure/MongoDbTKeyDocumentTestBase.cs b/CoreIntegrationTests/Infrastructure/MongoDbTKeyDocumentTestBase.cs index e4b0843..e048f14 100644 --- a/CoreIntegrationTests/Infrastructure/MongoDbTKeyDocumentTestBase.cs +++ b/CoreIntegrationTests/Infrastructure/MongoDbTKeyDocumentTestBase.cs @@ -1119,6 +1119,7 @@ namespace CoreIntegrationTests.Infrastructure #endregion Math #region Test Utils + [MethodImpl(MethodImplOptions.NoInlining)] private string GetCurrentMethod() { diff --git a/MongoDbGenericRepository/Abstractions/IKeyTypedReadOnlyMongoRepository.cs b/MongoDbGenericRepository/Abstractions/IKeyTypedReadOnlyMongoRepository.cs index a2b42c1..2b73f76 100644 --- a/MongoDbGenericRepository/Abstractions/IKeyTypedReadOnlyMongoRepository.cs +++ b/MongoDbGenericRepository/Abstractions/IKeyTypedReadOnlyMongoRepository.cs @@ -7,8 +7,7 @@ using MongoDbGenericRepository.Models; namespace MongoDbGenericRepository { - public interface IKeyTypedReadOnlyMongoRepository : IBaseReadOnlyRepository - where TKey : IEquatable + public interface IKeyTypedReadOnlyMongoRepository where TKey : IEquatable { #region Read diff --git a/MongoDbGenericRepository/Abstractions/IMongoDbCollectionIndexRepository.cs b/MongoDbGenericRepository/Abstractions/IMongoDbCollectionIndexRepository.cs index 01c0add..3fa09b9 100644 --- a/MongoDbGenericRepository/Abstractions/IMongoDbCollectionIndexRepository.cs +++ b/MongoDbGenericRepository/Abstractions/IMongoDbCollectionIndexRepository.cs @@ -9,7 +9,7 @@ namespace MongoDbGenericRepository /// /// The repository interface for managing indexes /// - public interface IMongoDbCollectionIndexRepository + public interface IMongoDbCollectionIndexRepository : IBaseMongoDbIndexRepository { /// /// Create a text index on the given field. @@ -24,21 +24,6 @@ namespace MongoDbGenericRepository Task CreateTextIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) where TDocument : IDocument; - /// - /// Create a text index on the given field. - /// IndexCreationOptions can be supplied to further specify - /// how the creation should be done. - /// - /// The type representing a Document. - /// The type of the primary key for a Document. - /// The field we want to index. - /// Options for creating an index. - /// An optional partition key. - /// The result of the create index operation. - Task CreateTextIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) - where TDocument : IDocument - where TKey : IEquatable; - /// /// Creates an index on the given field in ascending order. /// IndexCreationOptions can be supplied to further specify @@ -51,21 +36,6 @@ namespace MongoDbGenericRepository /// The result of the create index operation. Task CreateAscendingIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) where TDocument : IDocument; - /// - /// Creates an index on the given field in ascending order. - /// IndexCreationOptions can be supplied to further specify - /// how the creation should be done. - /// - /// The type representing a Document. - /// The type of the primary key for a Document. - /// The field we want to index. - /// Options for creating an index. - /// An optional partition key. - /// The result of the create index operation. - Task CreateAscendingIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) - where TDocument : IDocument - where TKey : IEquatable; - /// /// Creates an index on the given field in descending order. /// IndexCreationOptions can be supplied to further specify @@ -79,21 +49,6 @@ namespace MongoDbGenericRepository Task CreateDescendingIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) where TDocument : IDocument; - /// - /// Creates an index on the given field in descending order. - /// IndexCreationOptions can be supplied to further specify - /// how the creation should be done. - /// - /// The type representing a Document. - /// The type of the primary key for a Document. - /// The field we want to index. - /// Options for creating an index. - /// An optional partition key. - /// The result of the create index operation. - Task CreateDescendingIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) - where TDocument : IDocument - where TKey : IEquatable; - /// /// Creates a hashed index on the given field. /// IndexCreationOptions can be supplied to further specify @@ -107,21 +62,6 @@ namespace MongoDbGenericRepository Task CreateHashedIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) where TDocument : IDocument; - /// - /// Creates a hashed index on the given field. - /// IndexCreationOptions can be supplied to further specify - /// how the creation should be done. - /// - /// The type representing a Document. - /// The type of the primary key for a Document. - /// The field we want to index. - /// Options for creating an index. - /// An optional partition key. - /// The result of the create index operation. - Task CreateHashedIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) - where TDocument : IDocument - where TKey : IEquatable; - /// /// Creates a combined text index. /// IndexCreationOptions can be supplied to further specify @@ -135,21 +75,6 @@ namespace MongoDbGenericRepository Task CreateCombinedTextIndexAsync(IEnumerable>> fields, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) where TDocument : IDocument; - /// - /// Creates a combined text index. - /// IndexCreationOptions can be supplied to further specify - /// how the creation should be done. - /// - /// The type representing a Document. - /// The type of the primary key for a Document. - /// The fields we want to index. - /// Options for creating an index. - /// An optional partition key. - /// The result of the create index operation. - Task CreateCombinedTextIndexAsync(IEnumerable>> fields, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) - where TDocument : IDocument - where TKey : IEquatable; - /// /// Drops the index given a field name /// @@ -159,17 +84,6 @@ namespace MongoDbGenericRepository Task DropIndexAsync(string indexName, string partitionKey = null) where TDocument : IDocument; - /// - /// Drops the index given a field name - /// - /// The type representing a Document. - /// The type of the primary key for a Document. - /// The name of the index - /// An optional partition key - Task DropIndexAsync(string indexName, string partitionKey = null) - where TDocument : IDocument - where TKey : IEquatable; - /// /// Returns the names of the indexes present on a collection. /// @@ -178,16 +92,5 @@ namespace MongoDbGenericRepository /// A list containing the names of the indexes on on the concerned collection. Task> GetIndexesNamesAsync(string partitionKey = null) where TDocument : IDocument; - - /// - /// Returns the names of the indexes present on a collection. - /// - /// The type representing a Document. - /// The type of the primary key for a Document. - /// An optional partition key - /// A list containing the names of the indexes on on the concerned collection. - Task> GetIndexesNamesAsync(string partitionKey = null) - where TDocument : IDocument - where TKey : IEquatable; } } diff --git a/MongoDbGenericRepository/BaseMongoDbIndexRepository.cs b/MongoDbGenericRepository/BaseMongoDbIndexRepository.cs new file mode 100644 index 0000000..8edd0a7 --- /dev/null +++ b/MongoDbGenericRepository/BaseMongoDbIndexRepository.cs @@ -0,0 +1,230 @@ +using MongoDB.Driver; +using MongoDbGenericRepository.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; + +namespace MongoDbGenericRepository +{ + public interface IBaseMongoDbIndexRepository + { + /// + /// Create a text index on the given field. + /// IndexCreationOptions can be supplied to further specify + /// how the creation should be done. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The field we want to index. + /// Options for creating an index. + /// An optional partition key. + /// The result of the create index operation. + Task CreateTextIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable; + + /// + /// Creates an index on the given field in ascending order. + /// IndexCreationOptions can be supplied to further specify + /// how the creation should be done. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The field we want to index. + /// Options for creating an index. + /// An optional partition key. + /// The result of the create index operation. + Task CreateAscendingIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable; + + /// + /// Creates an index on the given field in descending order. + /// IndexCreationOptions can be supplied to further specify + /// how the creation should be done. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The field we want to index. + /// Options for creating an index. + /// An optional partition key. + /// The result of the create index operation. + Task CreateDescendingIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable; + + /// + /// Creates a hashed index on the given field. + /// IndexCreationOptions can be supplied to further specify + /// how the creation should be done. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The field we want to index. + /// Options for creating an index. + /// An optional partition key. + /// The result of the create index operation. + Task CreateHashedIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable; + + /// + /// Creates a combined text index. + /// IndexCreationOptions can be supplied to further specify + /// how the creation should be done. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The fields we want to index. + /// Options for creating an index. + /// An optional partition key. + /// The result of the create index operation. + Task CreateCombinedTextIndexAsync(IEnumerable>> fields, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable; + + /// + /// Drops the index given a field name + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The name of the index + /// An optional partition key + Task DropIndexAsync(string indexName, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable; + + /// + /// Returns the names of the indexes present on a collection. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// An optional partition key + /// A list containing the names of the indexes on on the concerned collection. + Task> GetIndexesNamesAsync(string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable; + } + + public class BaseMongoDbIndexRepository : BaseReadOnlyRepository, IBaseMongoDbIndexRepository + { + /// + /// The constructor taking a connection string and a database name. + /// + /// The connection string of the MongoDb server. + /// The name of the database against which you want to perform operations. + protected BaseMongoDbIndexRepository(string connectionString, string databaseName = null) : base(connectionString, databaseName) + { + } + + /// + /// The contructor taking a . + /// + /// A mongodb context implementing + protected BaseMongoDbIndexRepository(IMongoDbContext mongoDbContext) : base(mongoDbContext) + { + } + + /// + /// The contructor taking a . + /// + /// A mongodb context implementing + protected BaseMongoDbIndexRepository(IMongoDatabase mongoDatabase) : base(mongoDatabase) + { + } + + #region Index Management TKey + + /// + public async virtual Task> GetIndexesNamesAsync(string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + var indexCursor = await HandlePartitioned(partitionKey).Indexes.ListAsync(); + var indexes = await indexCursor.ToListAsync(); + return indexes.Select(e => e["name"].ToString()).ToList(); + } + + /// + public async virtual Task CreateTextIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await HandlePartitioned(partitionKey).Indexes + .CreateOneAsync( + new CreateIndexModel( + Builders.IndexKeys.Text(field), + indexCreationOptions == null ? null : MapIndexOptions(indexCreationOptions) + )); + } + + /// + public async virtual Task CreateAscendingIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + var collection = HandlePartitioned(partitionKey); + var createOptions = indexCreationOptions == null ? null : MapIndexOptions(indexCreationOptions); + var indexKey = Builders.IndexKeys; + return await collection.Indexes + .CreateOneAsync( + new CreateIndexModel(indexKey.Ascending(field), createOptions)); + } + + /// + public async virtual Task CreateDescendingIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + var collection = HandlePartitioned(partitionKey); + var createOptions = indexCreationOptions == null ? null : MapIndexOptions(indexCreationOptions); + var indexKey = Builders.IndexKeys; + return await collection.Indexes + .CreateOneAsync( + new CreateIndexModel(indexKey.Descending(field), createOptions)); + } + + + /// + public async virtual Task CreateHashedIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + var collection = HandlePartitioned(partitionKey); + var createOptions = indexCreationOptions == null ? null : MapIndexOptions(indexCreationOptions); + var indexKey = Builders.IndexKeys; + return await collection.Indexes + .CreateOneAsync( + new CreateIndexModel(indexKey.Hashed(field), createOptions)); + } + + /// + public async virtual Task CreateCombinedTextIndexAsync(IEnumerable>> fields, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + var collection = HandlePartitioned(partitionKey); + var createOptions = indexCreationOptions == null ? null : MapIndexOptions(indexCreationOptions); + var listOfDefs = new List>(); + foreach (var field in fields) + { + listOfDefs.Add(Builders.IndexKeys.Text(field)); + } + return await collection.Indexes + .CreateOneAsync(new CreateIndexModel(Builders.IndexKeys.Combine(listOfDefs), createOptions)); + } + + /// + public async virtual Task DropIndexAsync(string indexName, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + await HandlePartitioned(partitionKey).Indexes.DropOneAsync(indexName); + } + + #endregion Index Management TKey + + } +} diff --git a/MongoDbGenericRepository/BaseMongoDbRepository.IndexManagement.cs b/MongoDbGenericRepository/BaseMongoDbRepository.IndexManagement.cs index 440e143..b82294f 100644 --- a/MongoDbGenericRepository/BaseMongoDbRepository.IndexManagement.cs +++ b/MongoDbGenericRepository/BaseMongoDbRepository.IndexManagement.cs @@ -23,38 +23,12 @@ namespace MongoDbGenericRepository return await CreateTextIndexAsync(field, indexCreationOptions, partitionKey); } - /// - public async Task CreateTextIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) - where TDocument : IDocument - where TKey : IEquatable - { - return await HandlePartitioned(partitionKey).Indexes - .CreateOneAsync( - new CreateIndexModel( - Builders.IndexKeys.Text(field), - indexCreationOptions == null ? null : MapIndexOptions(indexCreationOptions) - )); - } - /// public async Task CreateAscendingIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) where TDocument : IDocument { return await CreateAscendingIndexAsync(field, indexCreationOptions, partitionKey); } - /// - public async Task CreateAscendingIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) - where TDocument : IDocument - where TKey : IEquatable - { - var collection = HandlePartitioned(partitionKey); - var createOptions = indexCreationOptions == null ? null : MapIndexOptions(indexCreationOptions); - var indexKey = Builders.IndexKeys; - return await collection.Indexes - .CreateOneAsync( - new CreateIndexModel(indexKey.Ascending(field), createOptions)); - } - /// public async Task CreateDescendingIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) where TDocument : IDocument @@ -62,18 +36,6 @@ namespace MongoDbGenericRepository return await CreateDescendingIndexAsync(field, indexCreationOptions, partitionKey); } - /// - public async Task CreateDescendingIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) - where TDocument : IDocument - where TKey : IEquatable - { - var collection = HandlePartitioned(partitionKey); - var createOptions = indexCreationOptions == null ? null : MapIndexOptions(indexCreationOptions); - var indexKey = Builders.IndexKeys; - return await collection.Indexes - .CreateOneAsync( - new CreateIndexModel(indexKey.Descending(field), createOptions)); - } /// public async Task CreateHashedIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) @@ -82,40 +44,12 @@ namespace MongoDbGenericRepository return await CreateHashedIndexAsync(field, indexCreationOptions, partitionKey); } - /// - public async Task CreateHashedIndexAsync(Expression> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) - where TDocument : IDocument - where TKey : IEquatable - { - var collection = HandlePartitioned(partitionKey); - var createOptions = indexCreationOptions == null ? null : MapIndexOptions(indexCreationOptions); - var indexKey = Builders.IndexKeys; - return await collection.Indexes - .CreateOneAsync( - new CreateIndexModel(indexKey.Hashed(field), createOptions)); - } - /// public async Task CreateCombinedTextIndexAsync(IEnumerable>> fields, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) where TDocument : IDocument { return await CreateCombinedTextIndexAsync(fields, indexCreationOptions, partitionKey); } - /// - public async Task CreateCombinedTextIndexAsync(IEnumerable>> fields, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) - where TDocument : IDocument - where TKey : IEquatable - { - var collection = HandlePartitioned(partitionKey); - var createOptions = indexCreationOptions == null ? null : MapIndexOptions(indexCreationOptions); - var listOfDefs = new List>(); - foreach (var field in fields) - { - listOfDefs.Add(Builders.IndexKeys.Text(field)); - } - return await collection.Indexes - .CreateOneAsync(new CreateIndexModel(Builders.IndexKeys.Combine(listOfDefs), createOptions)); - } /// public async Task DropIndexAsync(string indexName, string partitionKey = null) @@ -124,14 +58,6 @@ namespace MongoDbGenericRepository await DropIndexAsync(indexName, partitionKey); } - /// - public async Task DropIndexAsync(string indexName, string partitionKey = null) - where TDocument : IDocument - where TKey : IEquatable - { - await HandlePartitioned(partitionKey).Indexes.DropOneAsync(indexName); - } - /// public async Task> GetIndexesNamesAsync(string partitionKey = null) where TDocument : IDocument @@ -139,38 +65,8 @@ namespace MongoDbGenericRepository return await GetIndexesNamesAsync(partitionKey); } - /// - public async Task> GetIndexesNamesAsync(string partitionKey = null) - where TDocument : IDocument - where TKey : IEquatable - { - var indexCursor = await HandlePartitioned(partitionKey).Indexes.ListAsync(); - var indexes = await indexCursor.ToListAsync(); - return indexes.Select(e => e["name"].ToString()).ToList(); - } - - #endregion Index Management - private CreateIndexOptions MapIndexOptions(IndexCreationOptions indexCreationOptions) - { - return new CreateIndexOptions - { - Unique = indexCreationOptions.Unique, - TextIndexVersion = indexCreationOptions.TextIndexVersion, - SphereIndexVersion = indexCreationOptions.SphereIndexVersion, - Sparse = indexCreationOptions.Sparse, - Name = indexCreationOptions.Name, - Min = indexCreationOptions.Min, - Max = indexCreationOptions.Max, - LanguageOverride = indexCreationOptions.LanguageOverride, - ExpireAfter = indexCreationOptions.ExpireAfter, - DefaultLanguage = indexCreationOptions.DefaultLanguage, - BucketSize = indexCreationOptions.BucketSize, - Bits = indexCreationOptions.Bits, - Background = indexCreationOptions.Background, - Version = indexCreationOptions.Version - }; - } + } } \ No newline at end of file diff --git a/MongoDbGenericRepository/BaseMongoDbRepository.cs b/MongoDbGenericRepository/BaseMongoDbRepository.cs index db9dd3d..2fd1ad2 100644 --- a/MongoDbGenericRepository/BaseMongoDbRepository.cs +++ b/MongoDbGenericRepository/BaseMongoDbRepository.cs @@ -1,11 +1,13 @@ using MongoDB.Driver; +using MongoDbGenericRepository.DataAccess.Create; +using MongoDbGenericRepository.DataAccess.Update; +using MongoDbGenericRepository.Models; +using MongoDbGenericRepository.Utils; using System; using System.Collections.Generic; -using System.Threading.Tasks; -using System.Linq.Expressions; -using MongoDbGenericRepository.Models; using System.Linq; -using MongoDbGenericRepository.Utils; +using System.Linq.Expressions; +using System.Threading.Tasks; namespace MongoDbGenericRepository { @@ -15,6 +17,45 @@ namespace MongoDbGenericRepository /// public abstract partial class BaseMongoRepository : ReadOnlyMongoRepository, IBaseMongoRepository { + private object _initLock; + private MongoDbCreator _mongoDbCreator; + protected MongoDbCreator MongoDbCreator + { + get + { + if (_mongoDbCreator != null) { return _mongoDbCreator; } + + lock (_initLock) + { + if (_mongoDbCreator == null) + { + _mongoDbCreator = new MongoDbCreator(MongoDbContext); + } + } + + return _mongoDbCreator; + } + } + + private MongoDbUpdater _mongoDbUpdater; + protected MongoDbUpdater MongoDbUpdater + { + get + { + if (_mongoDbUpdater != null) { return _mongoDbUpdater; } + + lock (_initLock) + { + if (_mongoDbUpdater == null) + { + _mongoDbUpdater = new MongoDbUpdater(MongoDbContext); + } + } + + return _mongoDbUpdater; + } + } + /// /// The constructor taking a connection string and a database name. /// @@ -40,94 +81,6 @@ namespace MongoDbGenericRepository { } - #region Create - - /// - /// Asynchronously adds a document to the collection. - /// Populates the Id and AddedAtUtc fields if necessary. - /// - /// The type representing a Document. - /// The document you want to add. - public virtual async Task AddOneAsync(TDocument document) where TDocument : IDocument - { - FormatDocument(document); - await HandlePartitioned(document).InsertOneAsync(document); - } - - /// - /// Adds a document to the collection. - /// Populates the Id and AddedAtUtc fields if necessary. - /// - /// The type representing a Document. - /// The document you want to add. - public virtual void AddOne(TDocument document) where TDocument : IDocument - { - FormatDocument(document); - HandlePartitioned(document).InsertOne(document); - } - - /// - /// Asynchronously adds a list of documents to the collection. - /// Populates the Id and AddedAtUtc fields if necessary. - /// - /// The type representing a Document. - /// The documents you want to add. - public virtual async Task AddManyAsync(IEnumerable documents) where TDocument : IDocument - { - if (!documents.Any()) - { - return; - } - foreach (var document in documents) - { - FormatDocument(document); - } - // cannot use typeof(IPartitionedDocument).IsAssignableFrom(typeof(TDocument)), not available in netstandard 1.5 - if (documents.Any(e => e is IPartitionedDocument)) - { - foreach (var group in documents.GroupBy(e => ((IPartitionedDocument)e).PartitionKey)) - { - await HandlePartitioned(group.FirstOrDefault()).InsertManyAsync(group.ToList()); - } - } - else - { - await GetCollection().InsertManyAsync(documents.ToList()); - } - } - - /// - /// Adds a list of documents to the collection. - /// Populates the Id and AddedAtUtc fields if necessary. - /// - /// The type representing a Document. - /// The documents you want to add. - public virtual void AddMany(IEnumerable documents) where TDocument : IDocument - { - if (!documents.Any()) - { - return; - } - foreach (var document in documents) - { - FormatDocument(document); - } - // cannot use typeof(IPartitionedDocument).IsAssignableFrom(typeof(TDocument)), not available in netstandard 1.5 - if (documents.Any(e => e is IPartitionedDocument)) - { - foreach (var group in documents.GroupBy(e => ((IPartitionedDocument)e).PartitionKey)) - { - HandlePartitioned(group.FirstOrDefault()).InsertMany(group.ToList()); - } - } - else - { - GetCollection().InsertMany(documents.ToList()); - } - } - - #endregion Create - #region Create TKey /// @@ -141,8 +94,7 @@ namespace MongoDbGenericRepository where TDocument : IDocument where TKey : IEquatable { - FormatDocument(document); - await HandlePartitioned(document).InsertOneAsync(document); + await MongoDbCreator.AddOneAsync(document); } /// @@ -156,8 +108,7 @@ namespace MongoDbGenericRepository where TDocument : IDocument where TKey : IEquatable { - FormatDocument(document); - HandlePartitioned(document).InsertOne(document); + MongoDbCreator.AddOne(document); } /// @@ -171,26 +122,7 @@ namespace MongoDbGenericRepository where TDocument : IDocument where TKey : IEquatable { - if (!documents.Any()) - { - return; - } - foreach (var document in documents) - { - FormatDocument(document); - } - // cannot use typeof(IPartitionedDocument).IsAssignableFrom(typeof(TDocument)), not available in netstandard 1.5 - if (documents.Any(e => e is IPartitionedDocument)) - { - foreach (var group in documents.GroupBy(e => ((IPartitionedDocument)e).PartitionKey)) - { - await HandlePartitioned(group.FirstOrDefault()).InsertManyAsync(group.ToList()); - } - } - else - { - await GetCollection().InsertManyAsync(documents.ToList()); - } + await MongoDbCreator.AddManyAsync(documents); } /// @@ -204,26 +136,7 @@ namespace MongoDbGenericRepository where TDocument : IDocument where TKey : IEquatable { - if (!documents.Any()) - { - return; - } - foreach (var document in documents) - { - FormatDocument(document); - } - // cannot use typeof(IPartitionedDocument).IsAssignableFrom(typeof(TDocument)), not available in netstandard 1.5 - if (documents.Any(e => e is IPartitionedDocument)) - { - foreach (var group in documents.GroupBy(e => ((IPartitionedDocument)e).PartitionKey)) - { - HandlePartitioned(group.FirstOrDefault()).InsertMany(group.ToList()); - } - } - else - { - GetCollection().InsertMany(documents.ToList()); - } + MongoDbCreator.AddMany(documents); } #endregion @@ -237,8 +150,7 @@ namespace MongoDbGenericRepository /// The document with the modifications you want to persist. public virtual async Task UpdateOneAsync(TDocument modifiedDocument) where TDocument : IDocument { - var updateRes = await HandlePartitioned(modifiedDocument).ReplaceOneAsync(x => x.Id == modifiedDocument.Id, modifiedDocument); - return updateRes.ModifiedCount == 1; + return await MongoDbUpdater.UpdateOneAsync(modifiedDocument); } /// @@ -248,8 +160,7 @@ namespace MongoDbGenericRepository /// The document with the modifications you want to persist. public virtual bool UpdateOne(TDocument modifiedDocument) where TDocument : IDocument { - var updateRes = HandlePartitioned(modifiedDocument).ReplaceOne(x => x.Id == modifiedDocument.Id, modifiedDocument); - return updateRes.ModifiedCount == 1; + return MongoDbUpdater.UpdateOne(modifiedDocument); } /// @@ -261,9 +172,20 @@ namespace MongoDbGenericRepository public virtual async Task UpdateOneAsync(TDocument documentToModify, UpdateDefinition update) where TDocument : IDocument { - var filter = Builders.Filter.Eq("Id", documentToModify.Id); - var updateRes = await HandlePartitioned(documentToModify).UpdateOneAsync(filter, update); - return updateRes.ModifiedCount == 1; + return await MongoDbUpdater.UpdateOneAsync(documentToModify, update); + + } + + /// + /// Takes a document you want to modify and applies the update you have defined in MongoDb. + /// + /// The type representing a Document. + /// The document you want to modify. + /// The update definition for the document. + public virtual bool UpdateOne(TDocument documentToModify, UpdateDefinition update) + where TDocument : IDocument + { + return MongoDbUpdater.UpdateOne(documentToModify, update); } /// @@ -277,9 +199,7 @@ namespace MongoDbGenericRepository public virtual bool UpdateOne(TDocument documentToModify, Expression> field, TField value) where TDocument : IDocument { - var filter = Builders.Filter.Eq("Id", documentToModify.Id); - var updateRes = HandlePartitioned(documentToModify).UpdateOne(filter, Builders.Update.Set(field, value)); - return updateRes.ModifiedCount == 1; + return MongoDbUpdater.UpdateOne(documentToModify, field, value); } /// @@ -293,9 +213,7 @@ namespace MongoDbGenericRepository public virtual async Task UpdateOneAsync(TDocument documentToModify, Expression> field, TField value) where TDocument : IDocument { - var filter = Builders.Filter.Eq("Id", documentToModify.Id); - var updateRes = await HandlePartitioned(documentToModify).UpdateOneAsync(filter, Builders.Update.Set(field, value)); - return updateRes.ModifiedCount == 1; + return await MongoDbUpdater.UpdateOneAsync(documentToModify, field, value); } /// @@ -310,9 +228,7 @@ namespace MongoDbGenericRepository public virtual bool UpdateOne(FilterDefinition filter, Expression> field, TField value, string partitionKey = null) where TDocument : IDocument { - var collection = string.IsNullOrEmpty(partitionKey) ? GetCollection() : GetCollection(partitionKey); - var updateRes = collection.UpdateOne(filter, Builders.Update.Set(field, value)); - return updateRes.ModifiedCount == 1; + return MongoDbUpdater.UpdateOne(filter, field, value, partitionKey); } /// @@ -327,9 +243,7 @@ namespace MongoDbGenericRepository public virtual bool UpdateOne(Expression> filter, Expression> field, TField value, string partitionKey = null) where TDocument : IDocument { - var collection = string.IsNullOrEmpty(partitionKey) ? GetCollection() : GetCollection(partitionKey); - var updateRes = collection.UpdateOne(Builders.Filter.Where(filter), Builders.Update.Set(field, value)); - return updateRes.ModifiedCount == 1; + return MongoDbUpdater.UpdateOne(filter, field, value, partitionKey); } /// @@ -344,9 +258,7 @@ namespace MongoDbGenericRepository public virtual async Task UpdateOneAsync(FilterDefinition filter, Expression> field, TField value, string partitionKey = null) where TDocument : IDocument { - var collection = string.IsNullOrEmpty(partitionKey) ? GetCollection() : GetCollection(partitionKey); - var updateRes = await collection.UpdateOneAsync(filter, Builders.Update.Set(field, value)); - return updateRes.ModifiedCount == 1; + return await MongoDbUpdater.UpdateOneAsync(filter, field, value, partitionKey); } /// @@ -361,24 +273,9 @@ namespace MongoDbGenericRepository public virtual async Task UpdateOneAsync(Expression> filter, Expression> field, TField value, string partitionKey = null) where TDocument : IDocument { - var collection = string.IsNullOrEmpty(partitionKey) ? GetCollection() : GetCollection(partitionKey); - var updateRes = await collection.UpdateOneAsync(Builders.Filter.Where(filter), Builders.Update.Set(field, value)); - return updateRes.ModifiedCount == 1; + return await MongoDbUpdater.UpdateOneAsync(filter, field, value, partitionKey); } - /// - /// Takes a document you want to modify and applies the update you have defined in MongoDb. - /// - /// The type representing a Document. - /// The document you want to modify. - /// The update definition for the document. - public virtual bool UpdateOne(TDocument documentToModify, UpdateDefinition update) - where TDocument : IDocument - { - var filter = Builders.Filter.Eq("Id", documentToModify.Id); - var updateRes = HandlePartitioned(documentToModify).UpdateOne(filter, update, new UpdateOptions { IsUpsert = true }); - return updateRes.ModifiedCount == 1; - } #endregion Update @@ -394,9 +291,7 @@ namespace MongoDbGenericRepository where TDocument : IDocument where TKey : IEquatable { - var filter = Builders.Filter.Eq("Id", modifiedDocument.Id); - var updateRes = await HandlePartitioned(modifiedDocument).ReplaceOneAsync(filter, modifiedDocument); - return updateRes.ModifiedCount == 1; + return await MongoDbUpdater.UpdateOneAsync(modifiedDocument); } /// @@ -409,9 +304,7 @@ namespace MongoDbGenericRepository where TDocument : IDocument where TKey : IEquatable { - var filter = Builders.Filter.Eq("Id", modifiedDocument.Id); - var updateRes = HandlePartitioned(modifiedDocument).ReplaceOne(filter, modifiedDocument); - return updateRes.ModifiedCount == 1; + return MongoDbUpdater.UpdateOne(modifiedDocument); } /// @@ -425,9 +318,7 @@ namespace MongoDbGenericRepository where TDocument : IDocument where TKey : IEquatable { - var filter = Builders.Filter.Eq("Id", documentToModify.Id); - var updateRes = await HandlePartitioned(documentToModify).UpdateOneAsync(filter, update, new UpdateOptions { IsUpsert = true }); - return updateRes.ModifiedCount == 1; + return await MongoDbUpdater.UpdateOneAsync(documentToModify, update); } /// @@ -441,27 +332,7 @@ namespace MongoDbGenericRepository where TDocument : IDocument where TKey : IEquatable { - var filter = Builders.Filter.Eq("Id", documentToModify.Id); - var updateRes = HandlePartitioned(documentToModify).UpdateOne(filter, update, new UpdateOptions { IsUpsert = true }); - return updateRes.ModifiedCount == 1; - } - - /// - /// Updates the property field with the given value update a property field in entities. - /// - /// The type representing a Document. - /// The type of the primary key for a Document. - /// The type of the field. - /// The document you want to modify. - /// The field selector. - /// The new value of the property field. - public virtual async Task UpdateOneAsync(TDocument documentToModify, Expression> field, TField value) - where TDocument : IDocument - where TKey : IEquatable - { - var filter = Builders.Filter.Eq("Id", documentToModify.Id); - var updateRes = await HandlePartitioned(documentToModify).UpdateOneAsync(filter, Builders.Update.Set(field, value)); - return updateRes.ModifiedCount == 1; + return MongoDbUpdater.UpdateOne(documentToModify, update); } /// @@ -477,9 +348,57 @@ namespace MongoDbGenericRepository where TDocument : IDocument where TKey : IEquatable { - var filter = Builders.Filter.Eq("Id", documentToModify.Id); - var updateRes = HandlePartitioned(documentToModify).UpdateOne(filter, Builders.Update.Set(field, value)); - return updateRes.ModifiedCount == 1; + return MongoDbUpdater.UpdateOne(documentToModify, field, value); + } + + /// + /// Updates the property field with the given value update a property field in entities. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The type of the field. + /// The document you want to modify. + /// The field selector. + /// The new value of the property field. + public virtual async Task UpdateOneAsync(TDocument documentToModify, Expression> field, TField value) + where TDocument : IDocument + where TKey : IEquatable + { + return await MongoDbUpdater.UpdateOneAsync(documentToModify, field, value); + } + + /// + /// Updates the property field with the given value. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The type of the field. + /// The document filter. + /// The field selector. + /// The new value of the property field. + /// The value of the partition key. + public virtual bool UpdateOne(FilterDefinition filter, Expression> field, TField value, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return MongoDbUpdater.UpdateOne(filter, field, value, partitionKey); + } + + /// + /// For the entity selected by the filter, updates the property field with the given value. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The type of the field. + /// The document filter. + /// The field selector. + /// The new value of the property field. + /// The partition key for the document. + public virtual bool UpdateOne(Expression> filter, Expression> field, TField value, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return MongoDbUpdater.UpdateOne(filter, field, value, partitionKey); } /// @@ -496,9 +415,7 @@ namespace MongoDbGenericRepository where TDocument : IDocument where TKey : IEquatable { - var collection = string.IsNullOrEmpty(partitionKey) ? GetCollection() : GetCollection(partitionKey); - var updateRes = await collection.UpdateOneAsync(filter, Builders.Update.Set(field, value)); - return updateRes.ModifiedCount == 1; + return await MongoDbUpdater.UpdateOneAsync(filter, field, value, partitionKey); } /// @@ -518,42 +435,6 @@ namespace MongoDbGenericRepository return await UpdateOneAsync(Builders.Filter.Where(filter), field, value, partitionKey); } - /// - /// Updates the property field with the given value. - /// - /// The type representing a Document. - /// The type of the primary key for a Document. - /// The type of the field. - /// The document filter. - /// The field selector. - /// The new value of the property field. - /// The value of the partition key. - public virtual bool UpdateOne(FilterDefinition filter, Expression> field, TField value, string partitionKey = null) - where TDocument : IDocument - where TKey : IEquatable - { - var collection = string.IsNullOrEmpty(partitionKey) ? GetCollection() : GetCollection(partitionKey); - var updateRes = collection.UpdateOne(filter, Builders.Update.Set(field, value)); - return updateRes.ModifiedCount == 1; - } - - /// - /// For the entity selected by the filter, updates the property field with the given value. - /// - /// The type representing a Document. - /// The type of the primary key for a Document. - /// The type of the field. - /// The document filter. - /// The field selector. - /// The new value of the property field. - /// The partition key for the document. - public virtual bool UpdateOne(Expression> filter, Expression> field, TField value, string partitionKey = null) - where TDocument : IDocument - where TKey : IEquatable - { - return UpdateOne(Builders.Filter.Where(filter), field, value, partitionKey); - } - #endregion Update #region Delete @@ -1055,9 +936,9 @@ namespace MongoDbGenericRepository /// The number of documents you want to take. Default value is 50. /// An optional partition key. public virtual async Task> GetPaginatedAsync( - Expression> filter, - int skipNumber = 0, - int takeNumber = 50, + Expression> filter, + int skipNumber = 0, + int takeNumber = 50, string partitionKey = null) where TDocument : IDocument { diff --git a/MongoDbGenericRepository/BaseRepository.cs b/MongoDbGenericRepository/BaseReadOnlyRepository.cs similarity index 84% rename from MongoDbGenericRepository/BaseRepository.cs rename to MongoDbGenericRepository/BaseReadOnlyRepository.cs index 823c1f8..57ac920 100644 --- a/MongoDbGenericRepository/BaseRepository.cs +++ b/MongoDbGenericRepository/BaseReadOnlyRepository.cs @@ -1,5 +1,6 @@ using MongoDB.Driver; using MongoDB.Driver.Linq; +using MongoDbGenericRepository.DataAccess.Read; using MongoDbGenericRepository.Models; using System; using System.Collections.Generic; @@ -323,6 +324,11 @@ namespace MongoDbGenericRepository /// protected IMongoDbContext MongoDbContext = null; + /// + /// A MongoDb Reader for read operations + /// + protected MongoDbReader MongoDbReader = null; + /// /// The constructor taking a connection string and a database name. /// @@ -337,7 +343,7 @@ namespace MongoDbGenericRepository } ConnectionString = connectionString; DatabaseName = databaseName; - MongoDbContext = new MongoDbContext(connectionString, databaseName); + SetupMongoDbContext(new MongoDbContext(connectionString, databaseName)); } /// @@ -346,7 +352,7 @@ namespace MongoDbGenericRepository /// A mongodb context implementing protected BaseReadOnlyRepository(IMongoDbContext mongoDbContext) { - MongoDbContext = mongoDbContext; + SetupMongoDbContext(mongoDbContext); } /// @@ -355,9 +361,14 @@ namespace MongoDbGenericRepository /// A mongodb context implementing protected BaseReadOnlyRepository(IMongoDatabase mongoDatabase) { - MongoDbContext = new MongoDbContext(mongoDatabase); + SetupMongoDbContext(new MongoDbContext(mongoDatabase)); } + protected void SetupMongoDbContext(IMongoDbContext mongoDbContext) + { + MongoDbContext = mongoDbContext; + MongoDbReader = new MongoDbReader(MongoDbContext); + } #region Read TKey @@ -368,12 +379,11 @@ namespace MongoDbGenericRepository /// The type of the primary key for a Document. /// The Id of the document you want to get. /// An optional partition key. - public async Task GetByIdAsync(TKey id, string partitionKey = null) + public async virtual Task GetByIdAsync(TKey id, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { - var filter = Builders.Filter.Eq("Id", id); - return await HandlePartitioned(partitionKey).Find(filter).FirstOrDefaultAsync(); + return await MongoDbReader.GetByIdAsync(id, partitionKey); } /// @@ -383,12 +393,11 @@ namespace MongoDbGenericRepository /// The type of the primary key for a Document. /// The Id of the document you want to get. /// An optional partition key. - public TDocument GetById(TKey id, string partitionKey = null) + public virtual TDocument GetById(TKey id, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { - var filter = Builders.Filter.Eq("Id", id); - return HandlePartitioned(partitionKey).Find(filter).FirstOrDefault(); + return MongoDbReader.GetById(id, partitionKey); } /// @@ -398,11 +407,11 @@ namespace MongoDbGenericRepository /// The type of the primary key for a Document. /// A LINQ expression filter. /// An optional partition key. - public async Task GetOneAsync(Expression> filter, string partitionKey = null) + public async virtual Task GetOneAsync(Expression> filter, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { - return await HandlePartitioned(partitionKey).Find(filter).FirstOrDefaultAsync(); + return await MongoDbReader.GetOneAsync(filter, partitionKey); } /// @@ -412,11 +421,11 @@ namespace MongoDbGenericRepository /// The type of the primary key for a Document. /// A LINQ expression filter. /// An optional partition key. - public TDocument GetOne(Expression> filter, string partitionKey = null) + public virtual TDocument GetOne(Expression> filter, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { - return HandlePartitioned(partitionKey).Find(filter).FirstOrDefault(); + return MongoDbReader.GetOne(filter, partitionKey); } /// @@ -426,11 +435,11 @@ namespace MongoDbGenericRepository /// The type of the primary key for a Document. /// A LINQ expression filter. /// An optional partition key. - public IFindFluent GetCursor(Expression> filter, string partitionKey = null) + public virtual IFindFluent GetCursor(Expression> filter, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { - return HandlePartitioned(partitionKey).Find(filter); + return MongoDbReader.GetCursor(filter, partitionKey); } /// @@ -440,12 +449,11 @@ namespace MongoDbGenericRepository /// The type of the primary key for a Document. /// A LINQ expression filter. /// An optional partition key. - public async Task AnyAsync(Expression> filter, string partitionKey = null) + public async virtual Task AnyAsync(Expression> filter, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { - var count = await HandlePartitioned(partitionKey).CountDocumentsAsync(filter); - return (count > 0); + return await MongoDbReader.AnyAsync(filter, partitionKey); } /// @@ -455,12 +463,11 @@ namespace MongoDbGenericRepository /// The type of the primary key for a Document. /// A LINQ expression filter. /// An optional partition key. - public bool Any(Expression> filter, string partitionKey = null) + public virtual bool Any(Expression> filter, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { - var count = HandlePartitioned(partitionKey).CountDocuments(filter); - return (count > 0); + return MongoDbReader.Any(filter, partitionKey); } /// @@ -470,11 +477,11 @@ namespace MongoDbGenericRepository /// The type of the primary key for a Document. /// A LINQ expression filter. /// An optional partition key. - public async Task> GetAllAsync(Expression> filter, string partitionKey = null) + public async virtual Task> GetAllAsync(Expression> filter, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { - return await HandlePartitioned(partitionKey).Find(filter).ToListAsync(); + return await MongoDbReader.GetAllAsync(filter, partitionKey); } /// @@ -484,11 +491,11 @@ namespace MongoDbGenericRepository /// The type of the primary key for a Document. /// A LINQ expression filter. /// An optional partition key. - public List GetAll(Expression> filter, string partitionKey = null) + public virtual List GetAll(Expression> filter, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { - return HandlePartitioned(partitionKey).Find(filter).ToList(); + return MongoDbReader.GetAll(filter, partitionKey); } /// @@ -498,11 +505,11 @@ namespace MongoDbGenericRepository /// The type of the primary key for a Document. /// A LINQ expression filter. /// An optional partitionKey - public async Task CountAsync(Expression> filter, string partitionKey = null) + public async virtual Task CountAsync(Expression> filter, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { - return await HandlePartitioned(partitionKey).CountDocumentsAsync(filter); + return await MongoDbReader.CountAsync(filter, partitionKey); } /// @@ -512,11 +519,11 @@ namespace MongoDbGenericRepository /// The type of the primary key for a Document. /// A LINQ expression filter. /// An optional partitionKey - public long Count(Expression> filter, string partitionKey = null) + public virtual long Count(Expression> filter, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { - return HandlePartitioned(partitionKey).Find(filter).CountDocuments(); + return MongoDbReader.Count(filter, partitionKey); } /// @@ -527,14 +534,11 @@ namespace MongoDbGenericRepository /// A LINQ expression filter. /// A property selector to order by descending. /// An optional partitionKey. - public async Task GetByMaxAsync(Expression> filter, Expression> maxValueSelector, string partitionKey = null) + public async virtual Task GetByMaxAsync(Expression> filter, Expression> maxValueSelector, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { - return await GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) - .SortByDescending(maxValueSelector) - .Limit(1) - .FirstOrDefaultAsync(); + return await MongoDbReader.GetByMaxAsync(filter, maxValueSelector, partitionKey); } /// @@ -545,14 +549,11 @@ namespace MongoDbGenericRepository /// A LINQ expression filter. /// A property selector to order by descending. /// An optional partitionKey. - public TDocument GetByMax(Expression> filter, Expression> maxValueSelector, string partitionKey = null) + public virtual TDocument GetByMax(Expression> filter, Expression> maxValueSelector, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { - return GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) - .SortByDescending(maxValueSelector) - .Limit(1) - .FirstOrDefault(); + return MongoDbReader.GetByMax(filter, maxValueSelector, partitionKey); } /// @@ -563,14 +564,11 @@ namespace MongoDbGenericRepository /// A LINQ expression filter. /// A property selector to order by ascending. /// An optional partitionKey. - public async Task GetByMinAsync(Expression> filter, Expression> minValueSelector, string partitionKey = null) + public async virtual Task GetByMinAsync(Expression> filter, Expression> minValueSelector, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { - return await GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) - .SortBy(minValueSelector) - .Limit(1) - .FirstOrDefaultAsync(); + return await MongoDbReader.GetByMinAsync(filter, minValueSelector, partitionKey); } /// @@ -581,50 +579,11 @@ namespace MongoDbGenericRepository /// A LINQ expression filter. /// A property selector to order by ascending. /// An optional partitionKey. - public TDocument GetByMin(Expression> filter, Expression> minValueSelector, string partitionKey = null) + public virtual TDocument GetByMin(Expression> filter, Expression> minValueSelector, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { - return GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) - .SortBy(minValueSelector) - .Limit(1) - .FirstOrDefault(); - } - - /// - /// Gets the minimum value of a property in a mongodb collections that is satisfying the filter. - /// - /// The document type. - /// The type of the primary key. - /// The type of the value used to order the query. - /// A LINQ expression filter. - /// A property selector to order by ascending. - /// An optional partition key. - private IFindFluent GetMinMongoQuery(Expression> filter, Expression> minValueSelector, string partitionKey = null) - where TDocument : IDocument - where TKey : IEquatable - { - return GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) - .SortBy(ConvertExpression(minValueSelector)) - .Limit(1); - } - - /// - /// Gets the minimum value of a property in a mongodb collections that is satisfying the filter. - /// - /// The document type. - /// The type of the primary key. - /// The type of the value used to order the query. - /// A LINQ expression filter. - /// A property selector to order by descending. - /// An optional partition key. - private IFindFluent GetMaxMongoQuery(Expression> filter, Expression> maxValueSelector, string partitionKey = null) - where TDocument : IDocument - where TKey : IEquatable - { - return GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) - .SortByDescending(ConvertExpression(maxValueSelector)) - .Limit(1); + return MongoDbReader.GetByMin(filter, minValueSelector, partitionKey); } /// @@ -635,13 +594,11 @@ namespace MongoDbGenericRepository /// A LINQ expression filter. /// A property selector to order by ascending. /// An optional partitionKey. - public async Task GetMaxValueAsync(Expression> filter, Expression> maxValueSelector, string partitionKey = null) + public async virtual Task GetMaxValueAsync(Expression> filter, Expression> maxValueSelector, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { - return await GetMaxMongoQuery(filter, maxValueSelector, partitionKey) - .Project(maxValueSelector) - .FirstOrDefaultAsync(); + return await MongoDbReader.GetMaxValueAsync(filter, maxValueSelector, partitionKey); } /// @@ -653,14 +610,11 @@ namespace MongoDbGenericRepository /// A LINQ expression filter. /// A property selector to order by ascending. /// An optional partitionKey. - public TValue GetMaxValue(Expression> filter, Expression> maxValueSelector, string partitionKey = null) + public virtual TValue GetMaxValue(Expression> filter, Expression> maxValueSelector, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { - - return GetMaxMongoQuery(filter, maxValueSelector, partitionKey) - .Project(maxValueSelector) - .FirstOrDefault(); + return MongoDbReader.GetMaxValue(filter, maxValueSelector, partitionKey); } /// @@ -676,7 +630,7 @@ namespace MongoDbGenericRepository where TDocument : IDocument where TKey : IEquatable { - return await GetMinMongoQuery(filter, minValueSelector, partitionKey).Project(minValueSelector).FirstOrDefaultAsync(); + return await MongoDbReader.GetMinValueAsync(filter, minValueSelector, partitionKey); } /// @@ -692,7 +646,7 @@ namespace MongoDbGenericRepository where TDocument : IDocument where TKey : IEquatable { - return GetMinMongoQuery(filter, minValueSelector, partitionKey).Project(minValueSelector).FirstOrDefault(); + return MongoDbReader.GetMinValue(filter, minValueSelector, partitionKey); } #endregion @@ -712,7 +666,7 @@ namespace MongoDbGenericRepository where TDocument : IDocument where TKey : IEquatable { - return await GetQuery(filter, partitionKey).SumAsync(selector); + return await MongoDbReader.SumByAsync(filter, selector, partitionKey); } /// @@ -728,7 +682,7 @@ namespace MongoDbGenericRepository where TDocument : IDocument where TKey : IEquatable { - return GetQuery(filter, partitionKey).Sum(selector); + return MongoDbReader.SumBy(filter, selector, partitionKey); } /// @@ -744,7 +698,7 @@ namespace MongoDbGenericRepository where TDocument : IDocument where TKey : IEquatable { - return await GetQuery(filter, partitionKey).SumAsync(selector); + return await MongoDbReader.SumByAsync(filter, selector, partitionKey); } /// @@ -760,14 +714,14 @@ namespace MongoDbGenericRepository where TDocument : IDocument where TKey : IEquatable { - return GetQuery(filter, partitionKey).Sum(selector); + return MongoDbReader.SumBy(filter, selector, partitionKey); } - #endregion Sums TKey + #endregion Sum TKey #region Utility Methods - protected IMongoQueryable GetQuery(Expression> filter, string partitionKey = null) + protected virtual IMongoQueryable GetQuery(Expression> filter, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { @@ -830,7 +784,7 @@ namespace MongoDbGenericRepository /// The document type. /// The type of the value. /// The expression to convert - protected static Expression> ConvertExpression(Expression> expression) + protected virtual Expression> ConvertExpression(Expression> expression) { var param = expression.Parameters[0]; Expression body = expression.Body; @@ -838,6 +792,70 @@ namespace MongoDbGenericRepository return Expression.Lambda>(convert, param); } + /// + /// Maps a IndexCreationOptions object to a MongoDB.Driver.CreateIndexOptions object + /// + /// The options for creating an index. + /// + protected virtual CreateIndexOptions MapIndexOptions(IndexCreationOptions indexCreationOptions) + { + return new CreateIndexOptions + { + Unique = indexCreationOptions.Unique, + TextIndexVersion = indexCreationOptions.TextIndexVersion, + SphereIndexVersion = indexCreationOptions.SphereIndexVersion, + Sparse = indexCreationOptions.Sparse, + Name = indexCreationOptions.Name, + Min = indexCreationOptions.Min, + Max = indexCreationOptions.Max, + LanguageOverride = indexCreationOptions.LanguageOverride, + ExpireAfter = indexCreationOptions.ExpireAfter, + DefaultLanguage = indexCreationOptions.DefaultLanguage, + BucketSize = indexCreationOptions.BucketSize, + Bits = indexCreationOptions.Bits, + Background = indexCreationOptions.Background, + Version = indexCreationOptions.Version + }; + } + + + /// + /// Gets the minimum value of a property in a mongodb collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// The type of the value used to order the query. + /// A LINQ expression filter. + /// A property selector to order by ascending. + /// An optional partition key. + protected virtual IFindFluent GetMinMongoQuery(Expression> filter, Expression> minValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) + .SortBy(ConvertExpression(minValueSelector)) + .Limit(1); + } + + /// + /// Gets the minimum value of a property in a mongodb collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// The type of the value used to order the query. + /// A LINQ expression filter. + /// A property selector to order by descending. + /// An optional partition key. + protected virtual IFindFluent GetMaxMongoQuery(Expression> filter, Expression> maxValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) + .SortByDescending(ConvertExpression(maxValueSelector)) + .Limit(1); + } + + #endregion } } diff --git a/MongoDbGenericRepository/DataAccess/Base/DataAccessBase.cs b/MongoDbGenericRepository/DataAccess/Base/DataAccessBase.cs new file mode 100644 index 0000000..d6de4c6 --- /dev/null +++ b/MongoDbGenericRepository/DataAccess/Base/DataAccessBase.cs @@ -0,0 +1,158 @@ +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using MongoDbGenericRepository.Models; +using System; +using System.Linq; +using System.Linq.Expressions; + +namespace MongoDbGenericRepository.DataAccess.Base +{ + public class DataAccessBase + { + protected IMongoDbContext MongoDbContext; + + public DataAccessBase(IMongoDbContext mongoDbContext) + { + MongoDbContext = mongoDbContext; + } + + #region Utility Methods + + protected virtual IMongoQueryable GetQuery(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return GetCollection(partitionKey).AsQueryable().Where(filter); + } + + /// + /// Gets a collections for a potentially partitioned document type. + /// + /// The document type. + /// The type of the primary key. + /// The document. + /// + protected virtual IMongoCollection HandlePartitioned(TDocument document) + where TDocument : IDocument + where TKey : IEquatable + { + if (document is IPartitionedDocument) + { + return GetCollection(((IPartitionedDocument)document).PartitionKey); + } + return GetCollection(); + } + + /// + /// Gets a collections for the type TDocument with a partition key. + /// + /// The document type. + /// The type of the primary key. + /// The collection partition key. + /// + protected virtual IMongoCollection GetCollection(string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return MongoDbContext.GetCollection(partitionKey); + } + + /// + /// Gets a collections for a potentially partitioned document type. + /// + /// The document type. + /// The type of the primary key. + /// The collection partition key. + /// + protected virtual IMongoCollection HandlePartitioned(string partitionKey) + where TDocument : IDocument + where TKey : IEquatable + { + if (!string.IsNullOrEmpty(partitionKey)) + { + return GetCollection(partitionKey); + } + return GetCollection(); + } + + /// + /// Converts a LINQ expression of TDocument, TValue to a LINQ expression of TDocument, object + /// + /// The document type. + /// The type of the value. + /// The expression to convert + protected virtual Expression> ConvertExpression(Expression> expression) + { + var param = expression.Parameters[0]; + Expression body = expression.Body; + var convert = Expression.Convert(body, typeof(object)); + return Expression.Lambda>(convert, param); + } + + /// + /// Maps a IndexCreationOptions object to a MongoDB.Driver.CreateIndexOptions object + /// + /// The options for creating an index. + /// + protected virtual CreateIndexOptions MapIndexOptions(IndexCreationOptions indexCreationOptions) + { + return new CreateIndexOptions + { + Unique = indexCreationOptions.Unique, + TextIndexVersion = indexCreationOptions.TextIndexVersion, + SphereIndexVersion = indexCreationOptions.SphereIndexVersion, + Sparse = indexCreationOptions.Sparse, + Name = indexCreationOptions.Name, + Min = indexCreationOptions.Min, + Max = indexCreationOptions.Max, + LanguageOverride = indexCreationOptions.LanguageOverride, + ExpireAfter = indexCreationOptions.ExpireAfter, + DefaultLanguage = indexCreationOptions.DefaultLanguage, + BucketSize = indexCreationOptions.BucketSize, + Bits = indexCreationOptions.Bits, + Background = indexCreationOptions.Background, + Version = indexCreationOptions.Version + }; + } + + + /// + /// Gets the minimum value of a property in a mongodb collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// The type of the value used to order the query. + /// A LINQ expression filter. + /// A property selector to order by ascending. + /// An optional partition key. + protected virtual IFindFluent GetMinMongoQuery(Expression> filter, Expression> minValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) + .SortBy(ConvertExpression(minValueSelector)) + .Limit(1); + } + + /// + /// Gets the minimum value of a property in a mongodb collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// The type of the value used to order the query. + /// A LINQ expression filter. + /// A property selector to order by descending. + /// An optional partition key. + protected virtual IFindFluent GetMaxMongoQuery(Expression> filter, Expression> maxValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) + .SortByDescending(ConvertExpression(maxValueSelector)) + .Limit(1); + } + + + #endregion + } +} diff --git a/MongoDbGenericRepository/DataAccess/Create/MongoDbCreator.cs b/MongoDbGenericRepository/DataAccess/Create/MongoDbCreator.cs new file mode 100644 index 0000000..bc490c7 --- /dev/null +++ b/MongoDbGenericRepository/DataAccess/Create/MongoDbCreator.cs @@ -0,0 +1,142 @@ +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using MongoDbGenericRepository.DataAccess.Base; +using MongoDbGenericRepository.Models; +using MongoDbGenericRepository.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MongoDbGenericRepository.DataAccess.Create +{ + public class MongoDbCreator : DataAccessBase + { + public MongoDbCreator(IMongoDbContext mongoDbContext) : base(mongoDbContext) + { + } + + #region Create TKey + + /// + /// Asynchronously adds a document to the collection. + /// Populates the Id and AddedAtUtc fields if necessary. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The document you want to add. + public virtual async Task AddOneAsync(TDocument document) + where TDocument : IDocument + where TKey : IEquatable + { + FormatDocument(document); + await HandlePartitioned(document).InsertOneAsync(document); + } + + /// + /// Adds a document to the collection. + /// Populates the Id and AddedAtUtc fields if necessary. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The document you want to add. + public virtual void AddOne(TDocument document) + where TDocument : IDocument + where TKey : IEquatable + { + FormatDocument(document); + HandlePartitioned(document).InsertOne(document); + } + + /// + /// Asynchronously adds a list of documents to the collection. + /// Populates the Id and AddedAtUtc fields if necessary. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The documents you want to add. + public virtual async Task AddManyAsync(IEnumerable documents) + where TDocument : IDocument + where TKey : IEquatable + { + if (!documents.Any()) + { + return; + } + foreach (var document in documents) + { + FormatDocument(document); + } + // cannot use typeof(IPartitionedDocument).IsAssignableFrom(typeof(TDocument)), not available in netstandard 1.5 + if (documents.Any(e => e is IPartitionedDocument)) + { + foreach (var group in documents.GroupBy(e => ((IPartitionedDocument)e).PartitionKey)) + { + await HandlePartitioned(group.FirstOrDefault()).InsertManyAsync(group.ToList()); + } + } + else + { + await GetCollection().InsertManyAsync(documents.ToList()); + } + } + + /// + /// Adds a list of documents to the collection. + /// Populates the Id and AddedAtUtc fields if necessary. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The documents you want to add. + public virtual void AddMany(IEnumerable documents) + where TDocument : IDocument + where TKey : IEquatable + { + if (!documents.Any()) + { + return; + } + foreach (var document in documents) + { + FormatDocument(document); + } + // cannot use typeof(IPartitionedDocument).IsAssignableFrom(typeof(TDocument)), not available in netstandard 1.5 + if (documents.Any(e => e is IPartitionedDocument)) + { + foreach (var group in documents.GroupBy(e => ((IPartitionedDocument)e).PartitionKey)) + { + HandlePartitioned(group.FirstOrDefault()).InsertMany(group.ToList()); + } + } + else + { + GetCollection().InsertMany(documents.ToList()); + } + } + + #endregion + + /// + /// Sets the value of the document Id if it is not set already. + /// + /// The document type. + /// The type of the primary key. + /// The document. + protected void FormatDocument(TDocument document) + where TDocument : IDocument + where TKey : IEquatable + { + if (document == null) + { + throw new ArgumentNullException(nameof(document)); + } + var defaultTKey = default(TKey); + if (document.Id == null + || (defaultTKey != null + && defaultTKey.Equals(document.Id))) + { + document.Id = IdGenerator.GetId(); + } + } + } +} diff --git a/MongoDbGenericRepository/DataAccess/Delete/MongoDbEraser.cs b/MongoDbGenericRepository/DataAccess/Delete/MongoDbEraser.cs new file mode 100644 index 0000000..1daabac --- /dev/null +++ b/MongoDbGenericRepository/DataAccess/Delete/MongoDbEraser.cs @@ -0,0 +1,180 @@ +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using MongoDbGenericRepository.DataAccess.Base; +using MongoDbGenericRepository.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; + +namespace MongoDbGenericRepository.DataAccess.Delete +{ + public class MongoDbEraser : DataAccessBase + { + public MongoDbEraser(IMongoDbContext mongoDbContext) : base(mongoDbContext) + { + } + + #region Delete TKey + + /// + /// Deletes a document. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The document you want to delete. + /// The number of documents deleted. + public virtual long DeleteOne(TDocument document) + where TDocument : IDocument + where TKey : IEquatable + { + var filter = Builders.Filter.Eq("Id", document.Id); + return HandlePartitioned(document).DeleteOne(filter).DeletedCount; + } + + /// + /// Asynchronously deletes a document matching the condition of the LINQ expression filter. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The document you want to delete. + /// The number of documents deleted. + public virtual async Task DeleteOneAsync(TDocument document) + where TDocument : IDocument + where TKey : IEquatable + { + var filter = Builders.Filter.Eq("Id", document.Id); + return (await HandlePartitioned(document).DeleteOneAsync(filter)).DeletedCount; + } + + /// + /// Deletes a document matching the condition of the LINQ expression filter. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partition key. + /// The number of documents deleted. + public virtual long DeleteOne(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return HandlePartitioned(partitionKey).DeleteOne(filter).DeletedCount; + } + + /// + /// Asynchronously deletes a document matching the condition of the LINQ expression filter. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partition key. + /// The number of documents deleted. + public virtual async Task DeleteOneAsync(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return (await HandlePartitioned(partitionKey).DeleteOneAsync(filter)).DeletedCount; + } + + /// + /// Asynchronously deletes the documents matching the condition of the LINQ expression filter. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partition key. + /// The number of documents deleted. + public virtual async Task DeleteManyAsync(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return (await HandlePartitioned(partitionKey).DeleteManyAsync(filter)).DeletedCount; + } + + /// + /// Asynchronously deletes a list of documents. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The list of documents to delete. + /// The number of documents deleted. + public virtual async Task DeleteManyAsync(IEnumerable documents) + where TDocument : IDocument + where TKey : IEquatable + { + if (!documents.Any()) + { + return 0; + } + // cannot use typeof(IPartitionedDocument).IsAssignableFrom(typeof(TDocument)), not available in netstandard 1.5 + if (documents.Any(e => e is IPartitionedDocument)) + { + long deleteCount = 0; + foreach (var group in documents.GroupBy(e => ((IPartitionedDocument)e).PartitionKey)) + { + var groupIdsTodelete = group.Select(e => e.Id).ToArray(); + deleteCount += (await HandlePartitioned(group.FirstOrDefault()).DeleteManyAsync(x => groupIdsTodelete.Contains(x.Id))).DeletedCount; + } + return deleteCount; + } + else + { + var idsTodelete = documents.Select(e => e.Id).ToArray(); + return (await HandlePartitioned(documents.FirstOrDefault()).DeleteManyAsync(x => idsTodelete.Contains(x.Id))).DeletedCount; + } + } + + /// + /// Deletes a list of documents. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The list of documents to delete. + /// The number of documents deleted. + public virtual long DeleteMany(IEnumerable documents) + where TDocument : IDocument + where TKey : IEquatable + { + if (!documents.Any()) + { + return 0; + } + // cannot use typeof(IPartitionedDocument).IsAssignableFrom(typeof(TDocument)), not available in netstandard 1.5 + if (documents.Any(e => e is IPartitionedDocument)) + { + long deleteCount = 0; + foreach (var group in documents.GroupBy(e => ((IPartitionedDocument)e).PartitionKey)) + { + var groupIdsTodelete = group.Select(e => e.Id).ToArray(); + deleteCount += (HandlePartitioned(group.FirstOrDefault()).DeleteMany(x => groupIdsTodelete.Contains(x.Id))).DeletedCount; + } + return deleteCount; + } + else + { + var idsTodelete = documents.Select(e => e.Id).ToArray(); + return (HandlePartitioned(documents.FirstOrDefault()).DeleteMany(x => idsTodelete.Contains(x.Id))).DeletedCount; + } + } + + /// + /// Deletes the documents matching the condition of the LINQ expression filter. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partition key. + /// The number of documents deleted. + public virtual long DeleteMany(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return HandlePartitioned(partitionKey).DeleteMany(filter).DeletedCount; + } + + #endregion + + } +} diff --git a/MongoDbGenericRepository/DataAccess/Read/MongoDbReader.cs b/MongoDbGenericRepository/DataAccess/Read/MongoDbReader.cs new file mode 100644 index 0000000..a23340a --- /dev/null +++ b/MongoDbGenericRepository/DataAccess/Read/MongoDbReader.cs @@ -0,0 +1,393 @@ +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using MongoDbGenericRepository.DataAccess.Base; +using MongoDbGenericRepository.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; + +namespace MongoDbGenericRepository.DataAccess.Read +{ + public class MongoDbReader : DataAccessBase + { + public MongoDbReader(IMongoDbContext mongoDbContext) : base(mongoDbContext) + { + } + + #region Read TKey + + /// + /// Asynchronously returns one document given its id. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The Id of the document you want to get. + /// An optional partition key. + public async virtual Task GetByIdAsync(TKey id, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + var filter = Builders.Filter.Eq("Id", id); + return await HandlePartitioned(partitionKey).Find(filter).FirstOrDefaultAsync(); + } + + /// + /// Returns one document given its id. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The Id of the document you want to get. + /// An optional partition key. + public virtual TDocument GetById(TKey id, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + var filter = Builders.Filter.Eq("Id", id); + return HandlePartitioned(partitionKey).Find(filter).FirstOrDefault(); + } + + /// + /// Asynchronously returns one document given an expression filter. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partition key. + public async virtual Task GetOneAsync(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await HandlePartitioned(partitionKey).Find(filter).FirstOrDefaultAsync(); + } + + /// + /// Returns one document given an expression filter. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partition key. + public virtual TDocument GetOne(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return HandlePartitioned(partitionKey).Find(filter).FirstOrDefault(); + } + + /// + /// Returns a collection cursor. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partition key. + public virtual IFindFluent GetCursor(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return HandlePartitioned(partitionKey).Find(filter); + } + + /// + /// Returns true if any of the document of the collection matches the filter condition. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partition key. + public async virtual Task AnyAsync(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + var count = await HandlePartitioned(partitionKey).CountDocumentsAsync(filter); + return (count > 0); + } + + /// + /// Returns true if any of the document of the collection matches the filter condition. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partition key. + public virtual bool Any(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + var count = HandlePartitioned(partitionKey).CountDocuments(filter); + return (count > 0); + } + + /// + /// Asynchronously returns a list of the documents matching the filter condition. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partition key. + public async virtual Task> GetAllAsync(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await HandlePartitioned(partitionKey).Find(filter).ToListAsync(); + } + + /// + /// Returns a list of the documents matching the filter condition. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partition key. + public virtual List GetAll(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return HandlePartitioned(partitionKey).Find(filter).ToList(); + } + + /// + /// Asynchronously counts how many documents match the filter condition. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partitionKey + public async virtual Task CountAsync(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await HandlePartitioned(partitionKey).CountDocumentsAsync(filter); + } + + /// + /// Counts how many documents match the filter condition. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partitionKey + public virtual long Count(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return HandlePartitioned(partitionKey).Find(filter).CountDocuments(); + } + + #endregion + + #region Min / Max + + /// + /// Gets the document with the maximum value of a specified property in a MongoDB collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// A LINQ expression filter. + /// A property selector to order by descending. + /// An optional partitionKey. + public async virtual Task GetByMaxAsync(Expression> filter, Expression> maxValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) + .SortByDescending(maxValueSelector) + .Limit(1) + .FirstOrDefaultAsync(); + } + + /// + /// Gets the document with the maximum value of a specified property in a MongoDB collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// A LINQ expression filter. + /// A property selector to order by descending. + /// An optional partitionKey. + public virtual TDocument GetByMax(Expression> filter, Expression> maxValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) + .SortByDescending(maxValueSelector) + .Limit(1) + .FirstOrDefault(); + } + + /// + /// Gets the document with the minimum value of a specified property in a MongoDB collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// A LINQ expression filter. + /// A property selector to order by ascending. + /// An optional partitionKey. + public async virtual Task GetByMinAsync(Expression> filter, Expression> minValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) + .SortBy(minValueSelector) + .Limit(1) + .FirstOrDefaultAsync(); + } + + /// + /// Gets the document with the minimum value of a specified property in a MongoDB collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// A LINQ expression filter. + /// A property selector to order by ascending. + /// An optional partitionKey. + public virtual TDocument GetByMin(Expression> filter, Expression> minValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) + .SortBy(minValueSelector) + .Limit(1) + .FirstOrDefault(); + } + + /// + /// Gets the maximum value of a property in a mongodb collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// A LINQ expression filter. + /// A property selector to order by ascending. + /// An optional partitionKey. + public async virtual Task GetMaxValueAsync(Expression> filter, Expression> maxValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await GetMaxMongoQuery(filter, maxValueSelector, partitionKey) + .Project(maxValueSelector) + .FirstOrDefaultAsync(); + } + + /// + /// Gets the maximum value of a property in a mongodb collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// The type of the value used to order the query. + /// A LINQ expression filter. + /// A property selector to order by ascending. + /// An optional partitionKey. + public virtual TValue GetMaxValue(Expression> filter, Expression> maxValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return GetMaxMongoQuery(filter, maxValueSelector, partitionKey) + .Project(maxValueSelector) + .FirstOrDefault(); + } + + /// + /// Gets the minimum value of a property in a mongodb collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// The type of the value used to order the query. + /// A LINQ expression filter. + /// A property selector to order by ascending. + /// An optional partition key. + public virtual async Task GetMinValueAsync(Expression> filter, Expression> minValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await GetMinMongoQuery(filter, minValueSelector, partitionKey).Project(minValueSelector).FirstOrDefaultAsync(); + } + + /// + /// Gets the minimum value of a property in a mongodb collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// The type of the value used to order the query. + /// A LINQ expression filter. + /// A property selector to order by ascending. + /// An optional partition key. + public virtual TValue GetMinValue(Expression> filter, Expression> minValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return GetMinMongoQuery(filter, minValueSelector, partitionKey).Project(minValueSelector).FirstOrDefault(); + } + + + #endregion Min / Max + + #region Sum TKey + + /// + /// Sums the values of a selected field for a given filtered collection of documents. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// The field you want to sum. + /// The partition key of your document, if any. + public virtual async Task SumByAsync(Expression> filter, + Expression> selector, + string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await GetQuery(filter, partitionKey).SumAsync(selector); + } + + /// + /// Sums the values of a selected field for a given filtered collection of documents. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// The field you want to sum. + /// The partition key of your document, if any. + public virtual int SumBy(Expression> filter, + Expression> selector, + string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return GetQuery(filter, partitionKey).Sum(selector); + } + + /// + /// Sums the values of a selected field for a given filtered collection of documents. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// The field you want to sum. + /// The partition key of your document, if any. + public virtual async Task SumByAsync(Expression> filter, + Expression> selector, + string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await GetQuery(filter, partitionKey).SumAsync(selector); + } + + /// + /// Sums the values of a selected field for a given filtered collection of documents. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// The field you want to sum. + /// The partition key of your document, if any. + public virtual decimal SumBy(Expression> filter, + Expression> selector, + string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return GetQuery(filter, partitionKey).Sum(selector); + } + + #endregion Sum TKey + } +} diff --git a/MongoDbGenericRepository/DataAccess/Update/MongoDbUpdater.cs b/MongoDbGenericRepository/DataAccess/Update/MongoDbUpdater.cs new file mode 100644 index 0000000..0657132 --- /dev/null +++ b/MongoDbGenericRepository/DataAccess/Update/MongoDbUpdater.cs @@ -0,0 +1,190 @@ +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using MongoDbGenericRepository.DataAccess.Base; +using MongoDbGenericRepository.Models; +using MongoDbGenericRepository.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; + +namespace MongoDbGenericRepository.DataAccess.Update +{ + public class MongoDbUpdater : DataAccessBase + { + public MongoDbUpdater(IMongoDbContext mongoDbContext) : base(mongoDbContext) + { + } + + /// + /// Asynchronously Updates a document. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The document with the modifications you want to persist. + public virtual async Task UpdateOneAsync(TDocument modifiedDocument) + where TDocument : IDocument + where TKey : IEquatable + { + var filter = Builders.Filter.Eq("Id", modifiedDocument.Id); + var updateRes = await HandlePartitioned(modifiedDocument).ReplaceOneAsync(filter, modifiedDocument); + return updateRes.ModifiedCount == 1; + } + + /// + /// Updates a document. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The document with the modifications you want to persist. + public virtual bool UpdateOne(TDocument modifiedDocument) + where TDocument : IDocument + where TKey : IEquatable + { + var filter = Builders.Filter.Eq("Id", modifiedDocument.Id); + var updateRes = HandlePartitioned(modifiedDocument).ReplaceOne(filter, modifiedDocument); + return updateRes.ModifiedCount == 1; + } + + /// + /// Takes a document you want to modify and applies the update you have defined in MongoDb. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The document you want to modify. + /// The update definition for the document. + public virtual async Task UpdateOneAsync(TDocument documentToModify, UpdateDefinition update) + where TDocument : IDocument + where TKey : IEquatable + { + var filter = Builders.Filter.Eq("Id", documentToModify.Id); + var updateRes = await HandlePartitioned(documentToModify).UpdateOneAsync(filter, update, new UpdateOptions { IsUpsert = true }); + return updateRes.ModifiedCount == 1; + } + + /// + /// Takes a document you want to modify and applies the update you have defined in MongoDb. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The document you want to modify. + /// The update definition for the document. + public virtual bool UpdateOne(TDocument documentToModify, UpdateDefinition update) + where TDocument : IDocument + where TKey : IEquatable + { + var filter = Builders.Filter.Eq("Id", documentToModify.Id); + var updateRes = HandlePartitioned(documentToModify).UpdateOne(filter, update, new UpdateOptions { IsUpsert = true }); + return updateRes.ModifiedCount == 1; + } + + /// + /// Updates the property field with the given value update a property field in entities. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The type of the field. + /// The document you want to modify. + /// The field selector. + /// The new value of the property field. + public virtual async Task UpdateOneAsync(TDocument documentToModify, Expression> field, TField value) + where TDocument : IDocument + where TKey : IEquatable + { + var filter = Builders.Filter.Eq("Id", documentToModify.Id); + var updateRes = await HandlePartitioned(documentToModify).UpdateOneAsync(filter, Builders.Update.Set(field, value)); + return updateRes.ModifiedCount == 1; + } + + /// + /// Updates the property field with the given value update a property field in entities. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The type of the field. + /// The document you want to modify. + /// The field selector. + /// The new value of the property field. + public virtual bool UpdateOne(TDocument documentToModify, Expression> field, TField value) + where TDocument : IDocument + where TKey : IEquatable + { + var filter = Builders.Filter.Eq("Id", documentToModify.Id); + var updateRes = HandlePartitioned(documentToModify).UpdateOne(filter, Builders.Update.Set(field, value)); + return updateRes.ModifiedCount == 1; + } + + /// + /// Updates the property field with the given value update a property field in entities. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The type of the field. + /// The document filter. + /// The field selector. + /// The new value of the property field. + /// The value of the partition key. + public virtual async Task UpdateOneAsync(FilterDefinition filter, Expression> field, TField value, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + var collection = string.IsNullOrEmpty(partitionKey) ? GetCollection() : GetCollection(partitionKey); + var updateRes = await collection.UpdateOneAsync(filter, Builders.Update.Set(field, value)); + return updateRes.ModifiedCount == 1; + } + + /// + /// For the entity selected by the filter, updates the property field with the given value. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The type of the field. + /// The document filter. + /// The field selector. + /// The new value of the property field. + /// The partition key for the document. + public virtual async Task UpdateOneAsync(Expression> filter, Expression> field, TField value, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await UpdateOneAsync(Builders.Filter.Where(filter), field, value, partitionKey); + } + + /// + /// Updates the property field with the given value. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The type of the field. + /// The document filter. + /// The field selector. + /// The new value of the property field. + /// The value of the partition key. + public virtual bool UpdateOne(FilterDefinition filter, Expression> field, TField value, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + var collection = string.IsNullOrEmpty(partitionKey) ? GetCollection() : GetCollection(partitionKey); + var updateRes = collection.UpdateOne(filter, Builders.Update.Set(field, value)); + return updateRes.ModifiedCount == 1; + } + + /// + /// For the entity selected by the filter, updates the property field with the given value. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The type of the field. + /// The document filter. + /// The field selector. + /// The new value of the property field. + /// The partition key for the document. + public virtual bool UpdateOne(Expression> filter, Expression> field, TField value, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return UpdateOne(Builders.Filter.Where(filter), field, value, partitionKey); + } + } +} diff --git a/MongoDbGenericRepository/KeyTypedBaseMongoDbRepository.cs b/MongoDbGenericRepository/KeyTypedBaseMongoDbRepository.cs index 88d6f25..89cf73d 100644 --- a/MongoDbGenericRepository/KeyTypedBaseMongoDbRepository.cs +++ b/MongoDbGenericRepository/KeyTypedBaseMongoDbRepository.cs @@ -1,9 +1,10 @@ using MongoDB.Driver; +using MongoDbGenericRepository.DataAccess.Create; +using MongoDbGenericRepository.DataAccess.Update; using MongoDbGenericRepository.Models; -using MongoDbGenericRepository.Utils; using System; using System.Collections.Generic; -using System.Linq; +using System.Linq.Expressions; using System.Threading.Tasks; namespace MongoDbGenericRepository @@ -15,6 +16,45 @@ namespace MongoDbGenericRepository /// public abstract class KeyTypedBaseMongoDbRepository : KeyTypedReadOnlyMongoRepository, IKeyTypedReadOnlyMongoRepository where TKey : IEquatable { + protected object _initLock; + protected MongoDbCreator _mongoDbCreator; + protected MongoDbCreator MongoDbCreator + { + get + { + if(_mongoDbCreator == null) + { + lock (_initLock) + { + if(_mongoDbCreator == null) + { + _mongoDbCreator = new MongoDbCreator(MongoDbContext); + } + } + } + return _mongoDbCreator; + } + } + + private MongoDbUpdater _mongoDbUpdater; + protected MongoDbUpdater MongoDbUpdater + { + get + { + if (_mongoDbUpdater != null) { return _mongoDbUpdater; } + + lock (_initLock) + { + if (_mongoDbUpdater == null) + { + _mongoDbUpdater = new MongoDbUpdater(MongoDbContext); + } + } + + return _mongoDbUpdater; + } + } + /// /// The constructor taking a connection string and a database name. /// @@ -50,8 +90,7 @@ namespace MongoDbGenericRepository /// The document you want to add. public virtual async Task AddOneAsync(TDocument document) where TDocument : IDocument { - FormatDocument(document); - await HandlePartitioned(document).InsertOneAsync(document); + await MongoDbCreator.AddOneAsync(document); } /// @@ -62,8 +101,7 @@ namespace MongoDbGenericRepository /// The document you want to add. public virtual void AddOne(TDocument document) where TDocument : IDocument { - FormatDocument(document); - HandlePartitioned(document).InsertOne(document); + MongoDbCreator.AddOne(document); } /// @@ -74,26 +112,7 @@ namespace MongoDbGenericRepository /// The documents you want to add. public virtual async Task AddManyAsync(IEnumerable documents) where TDocument : IDocument { - if (!documents.Any()) - { - return; - } - foreach (var document in documents) - { - FormatDocument(document); - } - // cannot use typeof(IPartitionedDocument).IsAssignableFrom(typeof(TDocument)), not available in netstandard 1.5 - if (documents.Any(e => e is IPartitionedDocument)) - { - foreach (var group in documents.GroupBy(e => ((IPartitionedDocument)e).PartitionKey)) - { - await HandlePartitioned(group.FirstOrDefault()).InsertManyAsync(group.ToList()); - } - } - else - { - await GetCollection().InsertManyAsync(documents.ToList()); - } + await MongoDbCreator.AddManyAsync(documents); } /// @@ -104,48 +123,146 @@ namespace MongoDbGenericRepository /// The documents you want to add. public virtual void AddMany(IEnumerable documents) where TDocument : IDocument { - if (!documents.Any()) - { - return; - } - foreach (var document in documents) - { - FormatDocument(document); - } - // cannot use typeof(IPartitionedDocument).IsAssignableFrom(typeof(TDocument)), not available in netstandard 1.5 - if (documents.Any(e => e is IPartitionedDocument)) - { - foreach (var group in documents.GroupBy(e => ((IPartitionedDocument)e).PartitionKey)) - { - HandlePartitioned(group.FirstOrDefault()).InsertMany(group.ToList()); - } - } - else - { - GetCollection().InsertMany(documents.ToList()); - } + MongoDbCreator.AddMany(documents); } #endregion Create + #region Update + /// - /// Sets the value of the document Id if it is not set already. + /// Asynchronously Updates a document. /// - /// The document type. - /// The document. - protected void FormatDocument(TDocument document) where TDocument : IDocument + /// The type representing a Document. + /// The document with the modifications you want to persist. + public virtual async Task UpdateOneAsync(TDocument modifiedDocument) where TDocument : IDocument { - if (document == null) - { - throw new ArgumentNullException(nameof(document)); - } - var defaultTKey = default(TKey); - if (document.Id == null - || (defaultTKey != null - && defaultTKey.Equals(document.Id))) - { - document.Id = IdGenerator.GetId(); - } + return await MongoDbUpdater.UpdateOneAsync(modifiedDocument); } + + /// + /// Updates a document. + /// + /// The type representing a Document. + /// The document with the modifications you want to persist. + public virtual bool UpdateOne(TDocument modifiedDocument) where TDocument : IDocument + { + return MongoDbUpdater.UpdateOne(modifiedDocument); + } + + /// + /// Takes a document you want to modify and applies the update you have defined in MongoDb. + /// + /// The type representing a Document. + /// The document you want to modify. + /// The update definition for the document. + public virtual async Task UpdateOneAsync(TDocument documentToModify, UpdateDefinition update) + where TDocument : IDocument + { + return await MongoDbUpdater.UpdateOneAsync(documentToModify, update); + } + + /// + /// Takes a document you want to modify and applies the update you have defined in MongoDb. + /// + /// The type representing a Document. + /// The document you want to modify. + /// The update definition for the document. + public virtual bool UpdateOne(TDocument documentToModify, UpdateDefinition update) + where TDocument : IDocument + { + return MongoDbUpdater.UpdateOne(documentToModify, update); + } + + /// + /// Updates the property field with the given value update a property field in entities. + /// + /// The type representing a Document. + /// The type of the field. + /// The document you want to modify. + /// The field selector. + /// The new value of the property field. + public virtual bool UpdateOne(TDocument documentToModify, Expression> field, TField value) + where TDocument : IDocument + { + return MongoDbUpdater.UpdateOne(documentToModify, field, value); + } + + /// + /// Updates the property field with the given value update a property field in entities. + /// + /// The type representing a Document. + /// The type of the field. + /// The document you want to modify. + /// The field selector. + /// The new value of the property field. + public virtual async Task UpdateOneAsync(TDocument documentToModify, Expression> field, TField value) + where TDocument : IDocument + { + return await MongoDbUpdater.UpdateOneAsync(documentToModify, field, value); + } + + /// + /// Updates the property field with the given value update a property field in entities. + /// + /// The type representing a Document. + /// The type of the field. + /// The document filter. + /// The field selector. + /// The new value of the property field. + /// The value of the partition key. + public virtual bool UpdateOne(FilterDefinition filter, Expression> field, TField value, string partitionKey = null) + where TDocument : IDocument + { + return MongoDbUpdater.UpdateOne(filter, field, value, partitionKey); + } + + /// + /// For the entity selected by the filter, updates the property field with the given value. + /// + /// The type representing a Document. + /// The type of the field. + /// The document filter. + /// The field selector. + /// The new value of the property field. + /// The partition key for the document. + public virtual bool UpdateOne(Expression> filter, Expression> field, TField value, string partitionKey = null) + where TDocument : IDocument + { + return MongoDbUpdater.UpdateOne(filter, field, value, partitionKey); + } + + /// + /// Updates the property field with the given value update a property field in entities. + /// + /// The type representing a Document. + /// The type of the field. + /// The document filter. + /// The field selector. + /// The new value of the property field. + /// The value of the partition key. + public virtual async Task UpdateOneAsync(FilterDefinition filter, Expression> field, TField value, string partitionKey = null) + where TDocument : IDocument + { + return await MongoDbUpdater.UpdateOneAsync(filter, field, value, partitionKey); + } + + /// + /// For the entity selected by the filter, updates the property field with the given value. + /// + /// The type representing a Document. + /// The type of the field. + /// The document filter. + /// The field selector. + /// The new value of the property field. + /// The partition key for the document. + public virtual async Task UpdateOneAsync(Expression> filter, Expression> field, TField value, string partitionKey = null) + where TDocument : IDocument + { + return await MongoDbUpdater.UpdateOneAsync(filter, field, value, partitionKey); + } + + + #endregion Update } } diff --git a/MongoDbGenericRepository/KeyTypedReadOnlyMongoRepository.cs b/MongoDbGenericRepository/KeyTypedReadOnlyMongoRepository.cs index 3f3d37f..085c54a 100644 --- a/MongoDbGenericRepository/KeyTypedReadOnlyMongoRepository.cs +++ b/MongoDbGenericRepository/KeyTypedReadOnlyMongoRepository.cs @@ -1,9 +1,8 @@ using MongoDB.Driver; -using MongoDB.Driver.Linq; +using MongoDbGenericRepository.DataAccess.Read; using MongoDbGenericRepository.Models; using System; using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; @@ -13,14 +12,43 @@ namespace MongoDbGenericRepository /// The base Repository, it is meant to be inherited from by your custom custom MongoRepository implementation. /// Its constructor must be given a connection string and a database name. /// - public abstract class KeyTypedReadOnlyMongoRepository : BaseReadOnlyRepository, IKeyTypedReadOnlyMongoRepository where TKey : IEquatable + public abstract class KeyTypedReadOnlyMongoRepository : IKeyTypedReadOnlyMongoRepository where TKey : IEquatable { + /// + /// The connection string. + /// + public string ConnectionString { get; protected set; } + + /// + /// The database name. + /// + public string DatabaseName { get; protected set; } + + /// + /// The MongoDbContext + /// + protected IMongoDbContext MongoDbContext = null; + + /// + /// A MongoDb Reader for read operations + /// + protected MongoDbReader MongoDbReader = null; + /// /// The constructor taking a connection string and a database name. /// /// The connection string of the MongoDb server. /// The name of the database against which you want to perform operations. - protected KeyTypedReadOnlyMongoRepository(string connectionString, string databaseName = null) : base(connectionString, databaseName) + protected KeyTypedReadOnlyMongoRepository(string connectionString, string databaseName = null) + { + SetupMongoDbContext(connectionString, databaseName); + } + + /// + /// The contructor taking a . + /// + /// A mongodb context implementing + protected KeyTypedReadOnlyMongoRepository(IMongoDatabase mongoDatabase) : this(new MongoDbContext(mongoDatabase)) { } @@ -28,16 +56,27 @@ namespace MongoDbGenericRepository /// The contructor taking a . /// /// A mongodb context implementing - protected KeyTypedReadOnlyMongoRepository(IMongoDbContext mongoDbContext) : base(mongoDbContext) + protected KeyTypedReadOnlyMongoRepository(IMongoDbContext mongoDbContext) { + SetupMongoDbContext(mongoDbContext); } - /// - /// The contructor taking a . - /// - /// A mongodb context implementing - protected KeyTypedReadOnlyMongoRepository(IMongoDatabase mongoDatabase) : base(mongoDatabase) + protected void SetupMongoDbContext(IMongoDbContext mongoDbContext) { + MongoDbContext = MongoDbContext ?? mongoDbContext; + MongoDbReader = MongoDbReader ?? new MongoDbReader(MongoDbContext); + } + + protected void SetupMongoDbContext(string connectionString, string databaseName) + { + if (databaseName == null) + { + var mongoUrl = new MongoUrl(connectionString); + databaseName = databaseName ?? mongoUrl.DatabaseName; + } + ConnectionString = connectionString; + DatabaseName = databaseName; + SetupMongoDbContext(new MongoDbContext(connectionString, databaseName)); } #region Read @@ -50,7 +89,7 @@ namespace MongoDbGenericRepository /// An optional partition key. public async Task GetByIdAsync(TKey id, string partitionKey = null) where TDocument : IDocument { - return await base.GetByIdAsync(id, partitionKey); + return await MongoDbReader.GetByIdAsync(id, partitionKey); } /// @@ -61,7 +100,7 @@ namespace MongoDbGenericRepository /// An optional partition key. public TDocument GetById(TKey id, string partitionKey = null) where TDocument : IDocument { - return base.GetById(id, partitionKey); + return MongoDbReader.GetById(id, partitionKey); } /// @@ -72,7 +111,7 @@ namespace MongoDbGenericRepository /// An optional partition key. public async Task GetOneAsync(Expression> filter, string partitionKey = null) where TDocument : IDocument { - return await base.GetOneAsync(filter, partitionKey); + return await MongoDbReader.GetOneAsync(filter, partitionKey); } /// @@ -83,7 +122,7 @@ namespace MongoDbGenericRepository /// An optional partition key. public TDocument GetOne(Expression> filter, string partitionKey = null) where TDocument : IDocument { - return base.GetOne(filter, partitionKey); + return MongoDbReader.GetOne(filter, partitionKey); } /// @@ -94,7 +133,7 @@ namespace MongoDbGenericRepository /// An optional partition key. public IFindFluent GetCursor(Expression> filter, string partitionKey = null) where TDocument : IDocument { - return base.GetCursor(filter, partitionKey); + return MongoDbReader.GetCursor(filter, partitionKey); } /// @@ -105,7 +144,7 @@ namespace MongoDbGenericRepository /// An optional partition key. public async Task AnyAsync(Expression> filter, string partitionKey = null) where TDocument : IDocument { - return await base.AnyAsync(filter, partitionKey); + return await MongoDbReader.AnyAsync(filter, partitionKey); } /// @@ -116,7 +155,7 @@ namespace MongoDbGenericRepository /// An optional partition key. public bool Any(Expression> filter, string partitionKey = null) where TDocument : IDocument { - return base.Any(filter, partitionKey); + return MongoDbReader.Any(filter, partitionKey); } /// @@ -127,7 +166,7 @@ namespace MongoDbGenericRepository /// An optional partition key. public async Task> GetAllAsync(Expression> filter, string partitionKey = null) where TDocument : IDocument { - return await base.GetAllAsync(filter, partitionKey); + return await MongoDbReader.GetAllAsync(filter, partitionKey); } /// @@ -138,7 +177,7 @@ namespace MongoDbGenericRepository /// An optional partition key. public List GetAll(Expression> filter, string partitionKey = null) where TDocument : IDocument { - return base.GetAll(filter, partitionKey); + return MongoDbReader.GetAll(filter, partitionKey); } /// @@ -149,7 +188,7 @@ namespace MongoDbGenericRepository /// An optional partitionKey public async Task CountAsync(Expression> filter, string partitionKey = null) where TDocument : IDocument { - return await base.CountAsync(filter, partitionKey); + return await MongoDbReader.CountAsync(filter, partitionKey); } /// @@ -160,7 +199,7 @@ namespace MongoDbGenericRepository /// An optional partitionKey public long Count(Expression> filter, string partitionKey = null) where TDocument : IDocument { - return base.Count(filter, partitionKey); + return MongoDbReader.Count(filter, partitionKey); } /// @@ -173,7 +212,7 @@ namespace MongoDbGenericRepository public async Task GetByMaxAsync(Expression> filter, Expression> maxValueSelector, string partitionKey = null) where TDocument : IDocument { - return await base.GetByMaxAsync(filter, maxValueSelector, partitionKey); + return await MongoDbReader.GetByMaxAsync(filter, maxValueSelector, partitionKey); } /// @@ -187,7 +226,7 @@ namespace MongoDbGenericRepository public TDocument GetByMax(Expression> filter, Expression> maxValueSelector, string partitionKey = null) where TDocument : IDocument { - return base.GetByMax(filter, maxValueSelector, partitionKey); + return MongoDbReader.GetByMax(filter, maxValueSelector, partitionKey); } /// @@ -200,7 +239,7 @@ namespace MongoDbGenericRepository public async Task GetByMinAsync(Expression> filter, Expression> minValueSelector, string partitionKey = null) where TDocument : IDocument { - return await base.GetByMinAsync(filter, minValueSelector, partitionKey); + return await MongoDbReader.GetByMinAsync(filter, minValueSelector, partitionKey); } /// @@ -213,7 +252,7 @@ namespace MongoDbGenericRepository public TDocument GetByMin(Expression> filter, Expression> minValueSelector, string partitionKey = null) where TDocument : IDocument { - return base.GetByMin(filter, minValueSelector, partitionKey); + return MongoDbReader.GetByMin(filter, minValueSelector, partitionKey); } /// @@ -227,7 +266,7 @@ namespace MongoDbGenericRepository public async Task GetMaxValueAsync(Expression> filter, Expression> maxValueSelector, string partitionKey = null) where TDocument : IDocument { - return await base.GetMaxValueAsync(filter, maxValueSelector, partitionKey); + return await MongoDbReader.GetMaxValueAsync(filter, maxValueSelector, partitionKey); } /// @@ -241,7 +280,7 @@ namespace MongoDbGenericRepository public TValue GetMaxValue(Expression> filter, Expression> maxValueSelector, string partitionKey = null) where TDocument : IDocument { - return base.GetMaxValue(filter, maxValueSelector, partitionKey); + return MongoDbReader.GetMaxValue(filter, maxValueSelector, partitionKey); } /// @@ -255,7 +294,7 @@ namespace MongoDbGenericRepository public virtual async Task GetMinValueAsync(Expression> filter, Expression> minValueSelector, string partitionKey = null) where TDocument : IDocument { - return await base.GetMinValueAsync(filter, minValueSelector, partitionKey); + return await MongoDbReader.GetMinValueAsync(filter, minValueSelector, partitionKey); } /// @@ -269,7 +308,7 @@ namespace MongoDbGenericRepository public virtual TValue GetMinValue(Expression> filter, Expression> minValueSelector, string partitionKey = null) where TDocument : IDocument { - return base.GetMinValue(filter, minValueSelector, partitionKey); + return MongoDbReader.GetMinValue(filter, minValueSelector, partitionKey); } #endregion @@ -288,7 +327,7 @@ namespace MongoDbGenericRepository string partitionKey = null) where TDocument : IDocument { - return await base.SumByAsync(filter, selector, partitionKey); + return await MongoDbReader.SumByAsync(filter, selector, partitionKey); } /// @@ -303,7 +342,7 @@ namespace MongoDbGenericRepository string partitionKey = null) where TDocument : IDocument { - return await base.SumByAsync(filter, selector, partitionKey); + return await MongoDbReader.SumByAsync(filter, selector, partitionKey); } /// @@ -318,7 +357,7 @@ namespace MongoDbGenericRepository string partitionKey = null) where TDocument : IDocument { - return base.SumBy(filter, selector, partitionKey); + return MongoDbReader.SumBy(filter, selector, partitionKey); } /// @@ -333,58 +372,9 @@ namespace MongoDbGenericRepository string partitionKey = null) where TDocument : IDocument { - return base.SumBy(filter, selector, partitionKey); + return MongoDbReader.SumBy(filter, selector, partitionKey); } #endregion Maths - - #region Utility Methods - - /// - /// Gets a collections for the type TDocument with the matching partition key (if any). - /// - /// The document type. - /// An optional partition key. - /// An - protected virtual IMongoCollection GetCollection(string partitionKey = null) where TDocument : IDocument - { - return MongoDbContext.GetCollection(partitionKey); - } - - /// - /// Gets a collections for a potentially partitioned document type. - /// - /// The document type. - /// The type of the primary key. - /// The document. - /// - protected virtual IMongoCollection HandlePartitioned(TDocument document) - where TDocument : IDocument - { - if (document is IPartitionedDocument) - { - return GetCollection(((IPartitionedDocument)document).PartitionKey); - } - return GetCollection(); - } - - /// - /// Gets a collections for a potentially partitioned document type. - /// - /// The document type. - /// The type of the primary key. - /// The collection partition key. - /// - protected virtual IMongoCollection HandlePartitioned(string partitionKey) - where TDocument : IDocument - { - if (!string.IsNullOrEmpty(partitionKey)) - { - return GetCollection(partitionKey); - } - return GetCollection(); - } - - #endregion } } \ No newline at end of file diff --git a/MongoDbGenericRepository/MongoDbContext.cs b/MongoDbGenericRepository/MongoDbContext.cs index c5921b9..a7a5520 100644 --- a/MongoDbGenericRepository/MongoDbContext.cs +++ b/MongoDbGenericRepository/MongoDbContext.cs @@ -21,24 +21,6 @@ namespace MongoDbGenericRepository /// public IMongoDatabase Database { get; } - /// - /// Sets the Guid representation of the MongoDB Driver. - /// - /// The new value of the GuidRepresentation - public virtual void SetGuidRepresentation(MongoDB.Bson.GuidRepresentation guidRepresentation) - { - MongoDefaults.GuidRepresentation = guidRepresentation; - } - - /// - /// Initialize the Guid representation of the MongoDB Driver. - /// Override this method to change the default GuidRepresentation. - /// - protected virtual void InitializeGuidRepresentation() - { - // by default, avoid legacy UUID representation: use Binary 0x04 subtype. - MongoDefaults.GuidRepresentation = MongoDB.Bson.GuidRepresentation.Standard; - } /// /// The constructor of the MongoDbContext, it needs an object implementing . @@ -76,24 +58,12 @@ namespace MongoDbGenericRepository Database = client.GetDatabase(databaseName); } - /// - /// Extracts the CollectionName attribute from the entity type, if any. - /// - /// The type representing a Document. - /// The name of the collection in which the TDocument is stored. - private string GetAttributeCollectionName() - { - return (typeof(TDocument).GetTypeInfo() - .GetCustomAttributes(typeof(CollectionNameAttribute)) - .FirstOrDefault() as CollectionNameAttribute)?.Name; - } - /// /// Returns a collection for a document type. Also handles document types with a partition key. /// /// The type representing a Document. /// The optional value of the partition key. - public IMongoCollection GetCollection(string partitionKey = null) + public virtual IMongoCollection GetCollection(string partitionKey = null) { return Database.GetCollection(GetCollectionName(partitionKey)); } @@ -102,18 +72,49 @@ namespace MongoDbGenericRepository /// Drops a collection, use very carefully. /// /// The type representing a Document. - public void DropCollection(string partitionKey = null) + public virtual void DropCollection(string partitionKey = null) { Database.DropCollection(GetCollectionName(partitionKey)); } + /// + /// Sets the Guid representation of the MongoDB Driver. + /// + /// The new value of the GuidRepresentation + public virtual void SetGuidRepresentation(MongoDB.Bson.GuidRepresentation guidRepresentation) + { + MongoDefaults.GuidRepresentation = guidRepresentation; + } + + /// + /// Extracts the CollectionName attribute from the entity type, if any. + /// + /// The type representing a Document. + /// The name of the collection in which the TDocument is stored. + protected virtual string GetAttributeCollectionName() + { + return (typeof(TDocument).GetTypeInfo() + .GetCustomAttributes(typeof(CollectionNameAttribute)) + .FirstOrDefault() as CollectionNameAttribute)?.Name; + } + + /// + /// Initialize the Guid representation of the MongoDB Driver. + /// Override this method to change the default GuidRepresentation. + /// + protected virtual void InitializeGuidRepresentation() + { + // by default, avoid legacy UUID representation: use Binary 0x04 subtype. + MongoDefaults.GuidRepresentation = MongoDB.Bson.GuidRepresentation.Standard; + } + /// /// Given the document type and the partition key, returns the name of the collection it belongs to. /// /// The type representing a Document. /// The value of the partition key. /// The name of the collection. - private string GetCollectionName(string partitionKey) + protected virtual string GetCollectionName(string partitionKey) { var collectionName = GetAttributeCollectionName() ?? Pluralize(); if (string.IsNullOrEmpty(partitionKey)) @@ -128,7 +129,7 @@ namespace MongoDbGenericRepository /// /// The type representing a Document. /// The pluralized document name. - private string Pluralize() + protected virtual string Pluralize() { return (typeof(TDocument).Name.Pluralize()).Camelize(); } diff --git a/MongoDbGenericRepository/ReadOnlyMongoRepository.cs b/MongoDbGenericRepository/ReadOnlyMongoRepository.cs index fe995a9..4dfaf7e 100644 --- a/MongoDbGenericRepository/ReadOnlyMongoRepository.cs +++ b/MongoDbGenericRepository/ReadOnlyMongoRepository.cs @@ -1,5 +1,10 @@ using MongoDB.Driver; +using MongoDbGenericRepository.DataAccess.Read; +using MongoDbGenericRepository.Models; using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Threading.Tasks; namespace MongoDbGenericRepository { @@ -33,5 +38,356 @@ namespace MongoDbGenericRepository protected ReadOnlyMongoRepository(IMongoDatabase mongoDatabase) : base(mongoDatabase) { } + + + #region Read TKey + + /// + /// Asynchronously returns one document given its id. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The Id of the document you want to get. + /// An optional partition key. + public async virtual Task GetByIdAsync(TKey id, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await MongoDbReader.GetByIdAsync(id, partitionKey); + } + + /// + /// Returns one document given its id. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// The Id of the document you want to get. + /// An optional partition key. + public virtual TDocument GetById(TKey id, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return MongoDbReader.GetById(id, partitionKey); + } + + /// + /// Asynchronously returns one document given an expression filter. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partition key. + public async virtual Task GetOneAsync(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await MongoDbReader.GetOneAsync(filter, partitionKey); + } + + /// + /// Returns one document given an expression filter. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partition key. + public virtual TDocument GetOne(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return MongoDbReader.GetOne(filter, partitionKey); + } + + /// + /// Returns a collection cursor. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partition key. + public virtual IFindFluent GetCursor(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return MongoDbReader.GetCursor(filter, partitionKey); + } + + /// + /// Returns true if any of the document of the collection matches the filter condition. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partition key. + public async virtual Task AnyAsync(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await MongoDbReader.AnyAsync(filter, partitionKey); + } + + /// + /// Returns true if any of the document of the collection matches the filter condition. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partition key. + public virtual bool Any(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return MongoDbReader.Any(filter, partitionKey); + } + + /// + /// Asynchronously returns a list of the documents matching the filter condition. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partition key. + public async virtual Task> GetAllAsync(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await MongoDbReader.GetAllAsync(filter, partitionKey); + } + + /// + /// Returns a list of the documents matching the filter condition. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partition key. + public virtual List GetAll(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return MongoDbReader.GetAll(filter, partitionKey); + } + + /// + /// Asynchronously counts how many documents match the filter condition. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partitionKey + public async virtual Task CountAsync(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await MongoDbReader.CountAsync(filter, partitionKey); + } + + /// + /// Counts how many documents match the filter condition. + /// + /// The type representing a Document. + /// The type of the primary key for a Document. + /// A LINQ expression filter. + /// An optional partitionKey + public virtual long Count(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return MongoDbReader.Count(filter, partitionKey); + } + + /// + /// Gets the document with the maximum value of a specified property in a MongoDB collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// A LINQ expression filter. + /// A property selector to order by descending. + /// An optional partitionKey. + public async virtual Task GetByMaxAsync(Expression> filter, Expression> maxValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await MongoDbReader.GetByMaxAsync(filter, maxValueSelector, partitionKey); + } + + /// + /// Gets the document with the maximum value of a specified property in a MongoDB collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// A LINQ expression filter. + /// A property selector to order by descending. + /// An optional partitionKey. + public virtual TDocument GetByMax(Expression> filter, Expression> maxValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return MongoDbReader.GetByMax(filter, maxValueSelector, partitionKey); + } + + /// + /// Gets the document with the minimum value of a specified property in a MongoDB collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// A LINQ expression filter. + /// A property selector to order by ascending. + /// An optional partitionKey. + public async virtual Task GetByMinAsync(Expression> filter, Expression> minValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await MongoDbReader.GetByMinAsync(filter, minValueSelector, partitionKey); + } + + /// + /// Gets the document with the minimum value of a specified property in a MongoDB collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// A LINQ expression filter. + /// A property selector to order by ascending. + /// An optional partitionKey. + public virtual TDocument GetByMin(Expression> filter, Expression> minValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return MongoDbReader.GetByMin(filter, minValueSelector, partitionKey); + } + + /// + /// Gets the maximum value of a property in a mongodb collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// A LINQ expression filter. + /// A property selector to order by ascending. + /// An optional partitionKey. + public async virtual Task GetMaxValueAsync(Expression> filter, Expression> maxValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await MongoDbReader.GetMaxValueAsync(filter, maxValueSelector, partitionKey); + } + + /// + /// Gets the maximum value of a property in a mongodb collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// The type of the value used to order the query. + /// A LINQ expression filter. + /// A property selector to order by ascending. + /// An optional partitionKey. + public virtual TValue GetMaxValue(Expression> filter, Expression> maxValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return MongoDbReader.GetMaxValue(filter, maxValueSelector, partitionKey); + } + + /// + /// Gets the minimum value of a property in a mongodb collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// The type of the value used to order the query. + /// A LINQ expression filter. + /// A property selector to order by ascending. + /// An optional partition key. + public virtual async Task GetMinValueAsync(Expression> filter, Expression> minValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await MongoDbReader.GetMinValueAsync(filter, minValueSelector, partitionKey); + } + + /// + /// Gets the minimum value of a property in a mongodb collections that is satisfying the filter. + /// + /// The document type. + /// The type of the primary key. + /// The type of the value used to order the query. + /// A LINQ expression filter. + /// A property selector to order by ascending. + /// An optional partition key. + public virtual TValue GetMinValue(Expression> filter, Expression> minValueSelector, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return MongoDbReader.GetMinValue(filter, minValueSelector, partitionKey); + } + + #endregion + + #region Sum TKey + + /// + /// Sums the values of a selected field for a given filtered collection of documents. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// The field you want to sum. + /// The partition key of your document, if any. + public virtual async Task SumByAsync(Expression> filter, + Expression> selector, + string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await MongoDbReader.SumByAsync(filter, selector, partitionKey); + } + + /// + /// Sums the values of a selected field for a given filtered collection of documents. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// The field you want to sum. + /// The partition key of your document, if any. + public virtual int SumBy(Expression> filter, + Expression> selector, + string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return MongoDbReader.SumBy(filter, selector, partitionKey); + } + + /// + /// Sums the values of a selected field for a given filtered collection of documents. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// The field you want to sum. + /// The partition key of your document, if any. + public virtual async Task SumByAsync(Expression> filter, + Expression> selector, + string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return await MongoDbReader.SumByAsync(filter, selector, partitionKey); + } + + /// + /// Sums the values of a selected field for a given filtered collection of documents. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// The field you want to sum. + /// The partition key of your document, if any. + public virtual decimal SumBy(Expression> filter, + Expression> selector, + string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return MongoDbReader.SumBy(filter, selector, partitionKey); + } + + #endregion Sum TKey + } } \ No newline at end of file