Files
Core.BluePrint.Packages/Core.Blueprint.Mongo/Repositories/CollectionRepository.cs

275 lines
13 KiB
C#

using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace Core.Blueprint.Mongo
{
/// <summary>
/// Provides methods for interacting with a MongoDB collection for a specific document type.
/// Inherits from <see cref="MongoProvider"/> and implements <see cref="ICollectionsRepository{TDocument}"/>.
/// This class encapsulates common database operations such as querying, inserting, updating, and deleting documents.
/// </summary>
/// <typeparam name="TDocument">The type of document stored in the collection, which must implement <see cref="IDocument"/>.</typeparam>
public class CollectionRepository<TDocument>(IMongoDatabase database) : ICollectionsRepository<TDocument> where TDocument : IDocument
{
private IMongoCollection<TDocument> _collection;
/// <summary>
/// Initializes the MongoDB collection based on the <see cref="CollectionAttributeName"/> attribute
/// applied to the <typeparamref name="TDocument"/> type. Throws an exception if the attribute is not present.
/// </summary>
public void CollectionInitialization()
{
var collectionAttribute = typeof(TDocument).GetCustomAttribute<CollectionAttributeName>();
if (collectionAttribute == null)
{
throw new InvalidOperationException($"The class {typeof(TDocument).Name} is missing the CollectionAttributeName attribute.");
}
string collectionName = collectionAttribute.Name;
_collection = database.GetCollection<TDocument>(collectionName);
}
/// <summary>
/// Returns all documents from the collection as an enumerable list.
/// </summary>
/// <returns>A task that represents the asynchronous operation. The task result contains an enumerable list of documents.</returns>
public virtual async ValueTask<IEnumerable<TDocument>> AsQueryable()
{
return await _collection.AsQueryable().ToListAsync();
}
/// <summary>
/// Filters documents in the collection based on the provided filter expression.
/// </summary>
/// <param name="filterExpression">A lambda expression that defines the filter criteria for the documents.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains a list of documents that match the filter.</returns>
public virtual async Task<IEnumerable<TDocument>> FilterBy(
Expression<Func<TDocument, bool>> filterExpression)
{
var objectResult = await _collection.FindAsync(filterExpression).ConfigureAwait(false);
return objectResult.ToList();
}
/// <summary>
/// Filters documents in the collection based on the provided filter and projection expressions.
/// Projects the filtered documents into a new type.
/// </summary>
/// <typeparam name="TProjected">The type to project the documents into.</typeparam>
/// <param name="filterExpression">A lambda expression that defines the filter criteria for the documents.</param>
/// <param name="projectionExpression">A lambda expression that defines how the documents should be projected.</param>
/// <returns>An enumerable collection of projected documents.</returns>
public virtual IEnumerable<TProjected> FilterBy<TProjected>(
Expression<Func<TDocument, bool>> filterExpression,
Expression<Func<TDocument, TProjected>> projectionExpression)
{
return _collection.Find(filterExpression).Project(projectionExpression).ToEnumerable();
}
/// <summary>
/// Finds a single document that matches the provided filter expression.
/// </summary>
/// <param name="filterExpression">A lambda expression that defines the filter criteria for the document.</param>
/// <returns>The document that matches the filter, or <c>null</c> if no document is found.</returns>
public virtual TDocument FindOne(Expression<Func<TDocument, bool>> filterExpression)
{
return _collection.Find(filterExpression).FirstOrDefault();
}
/// <summary>
/// Filters documents in the collection based on the provided MongoDB filter definition.
/// </summary>
/// <param name="filterDefinition">A filter definition for MongoDB query.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains a list of documents that match the filter.</returns>
public virtual async Task<IEnumerable<TDocument>> FilterByMongoFilterAsync(FilterDefinition<TDocument> filterDefinition)
{
var objectResult = await _collection.Find(filterDefinition).ToListAsync().ConfigureAwait(false);
return objectResult;
}
/// <summary>
/// Asynchronously finds a single document that matches the provided filter expression.
/// </summary>
/// <param name="filterExpression">A lambda expression that defines the filter criteria for the document.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the document that matches the filter, or <c>null</c> if no document is found.</returns>
public virtual Task<TDocument> FindOneAsync(Expression<Func<TDocument, bool>> filterExpression)
{
return Task.Run(() => _collection.Find(filterExpression).FirstOrDefaultAsync());
}
/// <summary>
/// Finds a document by its unique identifier (ID).
/// </summary>
/// <param name="id">The unique identifier of the document.</param>
/// <returns>The document that matches the specified ID, or <c>null</c> if no document is found.</returns>
public virtual TDocument FindById(string id)
{
var filter = Builders<TDocument>.Filter.Eq(doc => doc._Id, id);
return _collection.Find(filter).SingleOrDefault();
}
/// <summary>
/// Asynchronously finds a document by its unique identifier (ID).
/// </summary>
/// <param name="id">The unique identifier of the document.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the document that matches the specified ID, or <c>null</c> if no document is found.</returns>
public virtual Task<TDocument> FindByIdAsync(string id)
{
return Task.Run(() =>
{
var objectId = new ObjectId(id);
var filter = Builders<TDocument>.Filter.Eq(doc => doc._Id, id);
return _collection.Find(filter).SingleOrDefaultAsync();
});
}
/// <summary>
/// Inserts a single document into the collection.
/// </summary>
/// <param name="document">The document to insert.</param>
public virtual void InsertOne(TDocument document)
{
_collection.InsertOne(document);
}
/// <summary>
/// Asynchronously inserts a single document into the collection.
/// </summary>
/// <param name="document">The document to insert.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public virtual Task InsertOneAsync(TDocument document)
{
return Task.Run(() => _collection.InsertOneAsync(document));
}
/// <summary>
/// Inserts multiple documents into the collection.
/// </summary>
/// <param name="documents">The collection of documents to insert.</param>
public void InsertMany(ICollection<TDocument> documents)
{
_collection.InsertMany(documents);
}
/// <summary>
/// Asynchronously inserts multiple documents into the collection.
/// </summary>
/// <param name="documents">The collection of documents to insert.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public virtual async Task InsertManyAsync(ICollection<TDocument> documents)
{
await _collection.InsertManyAsync(documents);
}
/// <summary>
/// Replaces an existing document in the collection.
/// </summary>
/// <param name="document">The document with the updated data.</param>
public void ReplaceOne(TDocument document)
{
var filter = Builders<TDocument>.Filter.Eq(doc => doc._Id, document._Id);
_collection.FindOneAndReplace(filter, document);
}
/// <summary>
/// Asynchronously replaces an existing document in the collection and returns the updated version.
/// </summary>
/// <param name="document">The document with the updated data. Its _Id is used to locate the existing document.</param>
/// <returns>
/// The updated document if the replacement was successful; otherwise, <c>null</c> if no matching document was found.
/// </returns>
public virtual async Task<TDocument?> ReplaceOneAsync(TDocument document)
{
var filter = Builders<TDocument>.Filter.Eq(doc => doc._Id, document._Id);
var options = new FindOneAndReplaceOptions<TDocument>
{
ReturnDocument = ReturnDocument.After // return the updated document
};
var result = await _collection.FindOneAndReplaceAsync(filter, document, options);
return result;
}
/// <summary>
/// Deletes a single document from the collection based on the provided filter expression.
/// </summary>
/// <param name="filterExpression">A lambda expression that defines the filter criteria for the document to delete.</param>
public void DeleteOne(Expression<Func<TDocument, bool>> filterExpression)
{
_collection.FindOneAndDelete(filterExpression);
}
/// <summary>
/// Asynchronously deletes a single document from the collection based on the provided filter expression.
/// </summary>
/// <param name="filterExpression">A lambda expression that defines the filter criteria for the document to delete.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public async Task<TDocument> DeleteOneAsync(Expression<Func<TDocument, bool>> filterExpression)
{
return await _collection.FindOneAndDeleteAsync(filterExpression);
}
/// <summary>
/// Deletes a single document from the collection based on its unique identifier (ID).
/// </summary>
/// <param name="id">The unique identifier of the document to delete.</param>
public void DeleteById(string id)
{
var objectId = new ObjectId(id);
var filter = Builders<TDocument>.Filter.Eq(doc => doc._Id, id);
_collection.FindOneAndDelete(filter);
}
/// <summary>
/// Asynchronously deletes a single document from the collection based on its unique identifier (ID).
/// </summary>
/// <param name="id">The unique identifier of the document to delete.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public Task DeleteByIdAsync(string id)
{
return Task.Run(() =>
{
var objectId = new ObjectId(id);
var filter = Builders<TDocument>.Filter.Eq(doc => doc._Id, id);
_collection.FindOneAndDeleteAsync(filter);
});
}
/// <summary>
/// Deletes multiple documents from the collection based on the provided filter expression.
/// </summary>
/// <param name="filterExpression">A lambda expression that defines the filter criteria for the documents to delete.</param>
public void DeleteMany(Expression<Func<TDocument, bool>> filterExpression)
{
_collection.DeleteMany(filterExpression);
}
/// <summary>
/// Asynchronously deletes multiple documents from the collection based on the provided filter expression.
/// </summary>
/// <param name="filterExpression">A lambda expression that defines the filter criteria for the documents to delete.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public Task DeleteManyAsync(Expression<Func<TDocument, bool>> filterExpression)
{
return Task.Run(() => _collection.DeleteManyAsync(filterExpression));
}
/// <summary>
/// Executes an aggregation pipeline and returns the first document in the result asynchronously.
/// </summary>
/// <typeparam name="TOutput">The type of the output document you expect from the pipeline.</typeparam>
/// <param name="pipeline">The aggregation pipeline definition to execute.</param>
/// <returns>The first document from the aggregation result, or null if none found.</returns>
public virtual Task<TOutput> FindOnePipelineAsync<TOutput>(PipelineDefinition<TDocument, TOutput> pipeline)
{
return Task.Run(() => _collection.Aggregate(pipeline).FirstOrDefaultAsync());
}
}
}