Add project files.

This commit is contained in:
Sergio Matias Urquin
2025-04-29 18:42:29 -06:00
parent 9c1958d351
commit 83fc1878c4
67 changed files with 4586 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
namespace Core.Blueprint.Mongo
{
/// <summary>
/// The <see cref="CollectionAttributeName"/> attribute is used to specify the name of a MongoDB collection
/// that a class should be mapped to. This attribute can be applied to classes that represent MongoDB entities.
/// </summary>
[AttributeUsage(AttributeTargets.Class)] // This attribute can only be applied to classes.
public class CollectionAttributeName : Attribute
{
/// <summary>
/// Gets or sets the name of the MongoDB collection that the class is mapped to.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="CollectionAttributeName"/> class with the specified collection name.
/// </summary>
/// <param name="name">The name of the MongoDB collection that the class should be mapped to.</param>
public CollectionAttributeName(string name)
{
Name = name ?? throw new ArgumentNullException(nameof(name), "Collection name cannot be null.");
}
}
}

View File

@@ -0,0 +1,121 @@
using Azure.Core;
using Azure.Identity;
using MongoDB.Driver.Authentication.Oidc;
namespace Core.Blueprint.Mongo.Configuration
{
/// <summary>
/// The <see cref="HeathIdentityProvider"/> class is responsible for acquiring an OpenID Connect (OIDC)
/// access token for MongoDB authentication using Azure Identity and Managed Identity credentials.
/// </summary>
public class HeathIdentityProvider : IOidcCallback
{
/// <summary>
/// The audience (resource identifier) for which the OIDC token is being requested.
/// </summary>
private readonly string _audience;
/// <summary>
/// The environment in which the application is running (e.g., Development, Production).
/// </summary>
private readonly string _environment;
/// <summary>
/// Initializes a new instance of the <see cref="HeathIdentityProvider"/> class with the specified audience.
/// </summary>
/// <param name="audience">The audience (resource identifier) for which the OIDC token is being requested.</param>
public HeathIdentityProvider(string audience)
{
_audience = audience;
_environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? string.Empty;
}
/// <summary>
/// Synchronously retrieves the OIDC access token to authenticate to MongoDB.
/// </summary>
/// <param name="parameters">The callback parameters provided for the OIDC request.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
/// <returns>An OIDC access token to authenticate to MongoDB.</returns>
/// <exception cref="Exception">Thrown if an error occurs during the token acquisition process.</exception>
public OidcAccessToken GetOidcAccessToken(OidcCallbackParameters parameters, CancellationToken cancellationToken)
{
try
{
AccessToken token;
TokenRequestContext tokenRequestContext =
new TokenRequestContext(
new[] { _audience }
);
if (_environment == "Local")
{
token =
new ChainedTokenCredential(
new ManagedIdentityCredential(),
new VisualStudioCredential(),
new VisualStudioCodeCredential(),
new SharedTokenCacheCredential()
)
.GetToken(tokenRequestContext);
}
else
{
token =
new ManagedIdentityCredential()
.GetToken(tokenRequestContext);
}
return new OidcAccessToken(token.Token, expiresIn: null);
}
catch (Exception ex)
{
throw new Exception($"An error occurred while trying to get the OIDC token to connect to the database, ERROR: {ex.Message}");
}
}
/// <summary>
/// Asynchronously retrieves the OIDC access token to authenticate to MongoDB.
/// </summary>
/// <param name="parameters">The callback parameters provided for the OIDC request.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
/// <returns>A task that represents the asynchronous operation, with an OIDC access token as the result.</returns>
/// <exception cref="Exception">Thrown if an error occurs during the token acquisition process.</exception>
public async Task<OidcAccessToken> GetOidcAccessTokenAsync(OidcCallbackParameters parameters, CancellationToken cancellationToken)
{
try
{
TokenRequestContext tokenRequestContext =
new TokenRequestContext(
new[] { _audience }
);
AccessToken token;
if (_environment == "Local")
{
token = await new ChainedTokenCredential(
new ManagedIdentityCredential(),
new VisualStudioCredential(),
new VisualStudioCodeCredential(),
new SharedTokenCacheCredential()
)
.GetTokenAsync(tokenRequestContext, cancellationToken)
.ConfigureAwait(false);
}
else
{
token = await new ManagedIdentityCredential()
.GetTokenAsync(tokenRequestContext, cancellationToken)
.ConfigureAwait(false);
}
return new OidcAccessToken(token.Token, expiresIn: null);
}
catch (Exception ex)
{
throw new Exception($"An error occurred while trying to get the OIDC token to connect to the database, ERROR: {ex.Message}");
}
}
}
}

View File

@@ -0,0 +1,65 @@
using Core.Blueprint.Mongo;
using Core.Blueprint.Mongo.Configuration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
namespace Core.Blueprint.DAL.Mongo.Configuration
{
/// <summary>
/// The <see cref="RegisterBlueprint"/> class contains extension methods for registering the MongoDB context and configuration settings
/// to the <see cref="IServiceCollection"/> in the dependency injection container.
/// </summary>
public static class RegisterBlueprint
{
/// <summary>
/// Adds the MongoDB layer services to the <see cref="IServiceCollection"/>.
/// Registers the MongoDB context and configuration settings for MongoDB connection, database name, and audience.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to which the services will be added.</param>
/// <param name="configuration">The <see cref="IConfiguration"/> used to load MongoDB settings.</param>
/// <returns>The updated <see cref="IServiceCollection"/> with MongoDB services registered.</returns>
public static IServiceCollection AddMongoLayer(this IServiceCollection services, IConfiguration configuration)
{
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? string.Empty;
services.AddSingleton<IMongoContext, MongoContext>();
var ConnectionString = configuration.GetSection("ConnectionStrings:MongoDB").Value ?? string.Empty;
var Databasename = configuration.GetSection("MongoDb:DatabaseName").Value ?? string.Empty;
var Audience = (environment == "Local")
? configuration.GetSection("MongoDb:LocalAudience").Value
: configuration.GetSection("MongoDb:Audience").Value;
if (string.IsNullOrEmpty(ConnectionString) || string.IsNullOrEmpty(Databasename) || string.IsNullOrEmpty(Audience))
throw new InvalidOperationException("Mongo connection is not configured correctly.");
services.Configure<MongoDbSettings>(options =>
{
options.ConnectionString = ConnectionString;
options.Databasename = Databasename;
options.Audience = Audience;
});
services.AddSingleton<IMongoClient>(serviceProvider =>
{
var settings = serviceProvider.GetRequiredService<IOptions<MongoDbSettings>>().Value;
var mongoClientSettings = MongoClientSettings.FromConnectionString(settings.ConnectionString);
mongoClientSettings.Credential = MongoCredential.CreateOidcCredential(new HeathIdentityProvider(settings.Audience));
return new MongoClient(mongoClientSettings);
});
services.AddSingleton<IMongoDatabase>(serviceProvider =>
{
var settings = serviceProvider.GetRequiredService<IOptions<MongoDbSettings>>().Value;
var client = serviceProvider.GetRequiredService<IMongoClient>();
return client.GetDatabase(settings.Databasename);
});
services.AddSingleton<IMongoDbSettings>(serviceProvider => serviceProvider.GetRequiredService<IOptions<MongoDbSettings>>().Value);
return services;
}
}
}

View File

@@ -0,0 +1,66 @@
using Microsoft.Extensions.Configuration;
namespace Core.Blueprint.Mongo
{
/// <summary>
/// The <see cref="MongoContext"/> class represents the MongoDB context that contains the connection information,
/// including the connection string, database name, and audience.
/// It implements the <see cref="IMongoContext"/> interface to provide methods for accessing these values.
/// </summary>
public sealed class MongoContext : IMongoContext
{
/// <summary>
/// Gets or sets the connection string used to connect to the MongoDB instance.
/// </summary>
public string ConnectionString { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the name of the MongoDB database.
/// </summary>
public string Databasename { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the audience (resource identifier) used for MongoDB authentication.
/// </summary>
public string Audience { get; set; } = string.Empty;
private readonly IConfiguration configuration;
/// <summary>
/// Initializes a new instance of the <see cref="MongoContext"/> class using the provided <see cref="IConfiguration"/>.
/// The configuration is used to retrieve MongoDB connection settings.
/// </summary>
/// <param name="configuration">The configuration used to retrieve the MongoDB connection settings.</param>
public MongoContext(IConfiguration configuration)
{
this.configuration = configuration;
}
/// <summary>
/// Retrieves the MongoDB connection string from the configuration.
/// </summary>
/// <returns>The MongoDB connection string, or an empty string if not found.</returns>
public string GetConnectionString()
{
return configuration.GetConnectionString("MongoDb:ConnectionString")?.ToString() ?? string.Empty;
}
/// <summary>
/// Retrieves the MongoDB database name from the configuration.
/// </summary>
/// <returns>The MongoDB database name, or an empty string if not found.</returns>
public string GetDatabasename()
{
return configuration.GetSection("MongoDb:DatabaseName").Value ?? string.Empty;
}
/// <summary>
/// Retrieves the MongoDB audience (resource identifier) from the configuration.
/// </summary>
/// <returns>The MongoDB audience, or an empty string if not found.</returns>
public string GetAudience()
{
return configuration.GetSection("MongoDb:Audience").Value ?? string.Empty;
}
}
}

View File

@@ -0,0 +1,25 @@
namespace Core.Blueprint.Mongo
{
/// <summary>
/// Represents the MongoDB configuration settings, including the connection string,
/// database name, and audience, used for connecting and authenticating to a MongoDB instance.
/// Implements the <see cref="IMongoDbSettings"/> interface to provide a strongly typed configuration.
/// </summary>
public class MongoDbSettings : IMongoDbSettings
{
/// <summary>
/// Gets or sets the connection string used to connect to the MongoDB instance.
/// </summary>
public string ConnectionString { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the name of the MongoDB database to connect to.
/// </summary>
public string Databasename { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the audience (resource identifier) used for MongoDB authentication.
/// </summary>
public string Audience { get; set; } = string.Empty;
}
}

View File

@@ -0,0 +1,152 @@
using MongoDB.Driver;
using System.Linq.Expressions;
namespace Core.Blueprint.Mongo
{
/// <summary>
/// Interface for performing CRUD operations and queries on MongoDB collections.
/// The <typeparamref name="TDocument"/> represents the type of documents in the collection,
/// which must implement the <see cref="IDocument"/> interface.
/// </summary>
/// <typeparam name="TDocument">The type of document in the MongoDB collection, must implement <see cref="IDocument"/>.</typeparam>
public interface ICollectionsRepository<TDocument> where TDocument : IDocument
{
/// <summary>
/// Retrieves all documents from the collection as an enumerable queryable result.
/// </summary>
/// <returns>A <see cref="ValueTask"/> containing an <see cref="IEnumerable{TDocument}"/> representing the collection's documents.</returns>
ValueTask<IEnumerable<TDocument>> AsQueryable();
/// <summary>
/// Filters the documents in the collection by the provided filter expression.
/// </summary>
/// <param name="filterExpression">An expression used to filter the documents based on the provided condition.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation, with a result of an <see cref="IEnumerable{TDocument}"/> of filtered documents.</returns>
Task<IEnumerable<TDocument>> FilterBy(
Expression<Func<TDocument, bool>> filterExpression);
/// <summary>
/// Filters the documents in the collection by the provided filter expression and projects them to a different type.
/// </summary>
/// <typeparam name="TProjected">The type to project the documents into.</typeparam>
/// <param name="filterExpression">An expression used to filter the documents.</param>
/// <param name="projectionExpression">An expression used to project the filtered documents into the <typeparamref name="TProjected"/> type.</param>
/// <returns>An <see cref="IEnumerable{TProjected}"/> representing the projected documents.</returns>
IEnumerable<TProjected> FilterBy<TProjected>(
Expression<Func<TDocument, bool>> filterExpression,
Expression<Func<TDocument, TProjected>> projectionExpression);
/// <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>
Task<IEnumerable<TDocument>> FilterByMongoFilterAsync(FilterDefinition<TDocument> filterDefinition);
/// <summary>
/// Finds a single document by the provided filter expression.
/// </summary>
/// <param name="filterExpression">An expression used to filter the documents.</param>
/// <returns>The first matching <see cref="TDocument"/> or null if no match is found.</returns>
TDocument FindOne(Expression<Func<TDocument, bool>> filterExpression);
/// <summary>
/// Asynchronously finds a single document by the provided filter expression.
/// </summary>
/// <param name="filterExpression">An expression used to filter the documents.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation, with the matching <see cref="TDocument"/> or null.</returns>
Task<TDocument> FindOneAsync(Expression<Func<TDocument, bool>> filterExpression);
/// <summary>
/// Finds a document by its identifier.
/// </summary>
/// <param name="id">The identifier of the document.</param>
/// <returns>The document with the provided identifier or null if not found.</returns>
TDocument FindById(string id);
/// <summary>
/// Asynchronously finds a document by its identifier.
/// </summary>
/// <param name="id">The identifier of the document.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation, with the matching <see cref="TDocument"/> or null.</returns>
Task<TDocument> FindByIdAsync(string id);
/// <summary>
/// Inserts a single document into the collection.
/// </summary>
/// <param name="document">The document to insert.</param>
void InsertOne(TDocument document);
/// <summary>
/// Asynchronously inserts a single document into the collection.
/// </summary>
/// <param name="document">The document to insert.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
Task InsertOneAsync(TDocument document);
/// <summary>
/// Inserts multiple documents into the collection.
/// </summary>
/// <param name="documents">The collection of documents to insert.</param>
void InsertMany(ICollection<TDocument> documents);
/// <summary>
/// Asynchronously inserts multiple documents into the collection.
/// </summary>
/// <param name="documents">The collection of documents to insert.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
Task InsertManyAsync(ICollection<TDocument> documents);
/// <summary>
/// Replaces an existing document with a new one.
/// </summary>
/// <param name="document">The document to replace the existing one.</param>
void ReplaceOne(TDocument document);
/// <summary>
/// Asynchronously replaces an existing document with a new one.
/// </summary>
/// <param name="document">The document to replace the existing one.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
Task ReplaceOneAsync(TDocument document);
/// <summary>
/// Deletes a single document by the provided filter expression.
/// </summary>
/// <param name="filterExpression">An expression used to filter the documents to delete.</param>
void DeleteOne(Expression<Func<TDocument, bool>> filterExpression);
/// <summary>
/// Asynchronously deletes a single document by the provided filter expression.
/// </summary>
/// <param name="filterExpression">An expression used to filter the documents to delete.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
Task<TDocument> DeleteOneAsync(Expression<Func<TDocument, bool>> filterExpression);
/// <summary>
/// Deletes a single document by its identifier.
/// </summary>
/// <param name="id">The identifier of the document to delete.</param>
void DeleteById(string id);
/// <summary>
/// Asynchronously deletes a single document by its identifier.
/// </summary>
/// <param name="id">The identifier of the document to delete.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
Task DeleteByIdAsync(string id);
/// <summary>
/// Deletes multiple documents that match the provided filter expression.
/// </summary>
/// <param name="filterExpression">An expression used to filter the documents to delete.</param>
void DeleteMany(Expression<Func<TDocument, bool>> filterExpression);
/// <summary>
/// Asynchronously deletes multiple documents that match the provided filter expression.
/// </summary>
/// <param name="filterExpression">An expression used to filter the documents to delete.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
Task DeleteManyAsync(Expression<Func<TDocument, bool>> filterExpression);
}
}

View File

@@ -0,0 +1,39 @@
using Core.Blueprint.Mongo;
public interface IDocument
{
/// <summary>
/// Gets or sets the MongoDB ObjectId for the document.
/// </summary>
string _Id { get; }
/// <summary>
/// Gets or sets a unique identifier for the document, represented as a string (GUID).
/// </summary>
string Id { get; }
/// <summary>
/// Gets or sets the timestamp of when the document was created.
/// </summary>
DateTime CreatedAt { get; }
/// <summary>
/// Gets or sets the user or system who created the document.
/// </summary>
string? CreatedBy { get; set; }
/// <summary>
/// Gets or sets the timestamp of when the document was last updated.
/// </summary>
DateTime? UpdatedAt { get; set; }
/// <summary>
/// Gets or sets the user or system who last updated the document.
/// </summary>
string? UpdatedBy { get; set; }
/// <summary>
/// Gets or sets the status of the document.
/// </summary>
StatusEnum? Status { get; set; }
}

View File

@@ -0,0 +1,45 @@
namespace Core.Blueprint.Mongo
{
/// <summary>
/// Represents the context for interacting with MongoDB, providing access to connection-related information,
/// such as the connection string, database name, and audience for authentication.
/// </summary>
public interface IMongoContext
{
/// <summary>
/// Gets the connection string used to connect to the MongoDB instance.
/// </summary>
/// <returns>A string representing the MongoDB connection string.</returns>
string GetConnectionString();
/// <summary>
/// Gets the name of the MongoDB database.
/// </summary>
/// <returns>A string representing the MongoDB database name.</returns>
string GetDatabasename();
/// <summary>
/// Gets the audience (resource identifier) used for MongoDB authentication.
/// </summary>
/// <returns>A string representing the MongoDB audience (typically the resource identifier for authentication).</returns>
string GetAudience();
/// <summary>
/// Gets or sets the MongoDB connection string used to connect to the database.
/// </summary>
/// <value>A string representing the MongoDB connection string.</value>
string ConnectionString { get; set; }
/// <summary>
/// Gets or sets the name of the MongoDB database.
/// </summary>
/// <value>A string representing the MongoDB database name.</value>
string Databasename { get; set; }
/// <summary>
/// Gets or sets the audience (resource identifier) for MongoDB authentication.
/// </summary>
/// <value>A string representing the MongoDB audience (resource identifier for authentication).</value>
string Audience { get; set; }
}
}

View File

@@ -0,0 +1,32 @@
namespace Core.Blueprint.Mongo
{
/// <summary>
/// Represents the settings required to connect to a MongoDB instance, including the connection string,
/// database name, and audience used for authentication.
/// </summary>
public interface IMongoDbSettings
{
/// <summary>
/// Gets or sets the connection string used to connect to the MongoDB instance.
/// The connection string includes details such as server address, port,
/// authentication credentials, and any additional options needed for connection.
/// </summary>
/// <value>A string representing the MongoDB connection string.</value>
string ConnectionString { get; set; }
/// <summary>
/// Gets or sets the name of the MongoDB database to connect to.
/// This value specifies which database to use within the MongoDB instance.
/// </summary>
/// <value>A string representing the name of the MongoDB database.</value>
string Databasename { get; set; }
/// <summary>
/// Gets or sets the audience (resource identifier) for MongoDB authentication.
/// This is typically used in token-based authentication schemes (e.g., OAuth or OpenID Connect),
/// to specify the intended recipient of an access token.
/// </summary>
/// <value>A string representing the MongoDB audience (resource identifier for authentication).</value>
string Audience { get; set; }
}
}

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Core" Version="1.44.1" />
<PackageReference Include="Azure.Identity" Version="1.13.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="2.1.0" />
<PackageReference Include="MongoDB.Bson" Version="3.0.0" />
<PackageReference Include="MongoDB.Driver" Version="3.0.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,81 @@
using Core.Blueprint.Mongo;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using System.Text.Json.Serialization;
public class Document : IDocument
{
/// <summary>
/// Gets or sets the MongoDB ObjectId for the document.
/// This property is automatically generated if not provided.
/// It is used as the primary key for the document in MongoDB.
/// </summary>
[BsonId]
[BsonElement("_id")]
[BsonRepresentation(BsonType.ObjectId)]
[JsonPropertyName("_id")]
public string _Id { get; init; }
/// <summary>
/// Gets or sets a unique identifier for the document, represented as a string (GUID).
/// This value is automatically generated if not provided and can be used as a secondary key.
/// </summary>
[BsonElement("id")]
[BsonRepresentation(BsonType.String)]
[JsonPropertyName("id")]
public string Id { get; init; }
/// <summary>
/// Gets or sets the timestamp of when the document was created.
/// This value is automatically set to the current UTC time when the document is created.
/// </summary>
[BsonElement("createdAt")]
[BsonRepresentation(BsonType.DateTime)]
[JsonPropertyName("createdAt")]
public DateTime CreatedAt { get; init; }
/// <summary>
/// Gets or sets the user or system who created the document.
/// This field can be used for audit purposes.
/// </summary>
[BsonElement("createdBy")]
[BsonRepresentation(BsonType.String)]
[JsonPropertyName("createdBy")]
public string? CreatedBy { get; set; }
/// <summary>
/// Gets or sets the timestamp of when the document was last updated.
/// This value is nullable and will be set to null if the document has never been updated.
/// </summary>
[BsonElement("updatedAt")]
[BsonRepresentation(BsonType.DateTime)]
[JsonPropertyName("updatedAt")]
public DateTime? UpdatedAt { get; set; } = null;
/// <summary>
/// Gets or sets the user or system who last updated the document.
/// This field can be used for audit purposes.
/// </summary>
[BsonElement("updatedBy")]
[BsonRepresentation(BsonType.String)]
[JsonPropertyName("updatedBy")]
public string? UpdatedBy { get; set; } = null;
/// <summary>
/// Gets or sets the status of the document.
/// The status is represented by an enum and defaults to <see cref="StatusEnum.Active"/>.
/// </summary>
[BsonElement("status")]
[BsonRepresentation(BsonType.String)]
[JsonPropertyName("status")]
[JsonConverter(typeof(JsonStringEnumConverter))]
public StatusEnum? Status { get; set; }
public Document()
{
_Id = ObjectId.GenerateNewId().ToString();
Id = Guid.NewGuid().ToString();
CreatedAt = DateTime.UtcNow;
Status = StatusEnum.Active;
}
}

View File

@@ -0,0 +1,28 @@
using System.Text.Json.Serialization;
namespace Core.Blueprint.Mongo
{
/// <summary>
/// Represents the status of a document or entity. This enum is used to track the state of a document
/// within the system. The `JsonStringEnumConverter` ensures that the enum values are serialized as strings
/// in JSON format, rather than as numeric values.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum StatusEnum
{
/// <summary>
/// Represents an active document or entity.
/// </summary>
Active = 0,
/// <summary>
/// Represents an inactive document or entity.
/// </summary>
Inactive = 1,
/// <summary>
/// Represents a deleted document or entity.
/// </summary>
Deleted = 2
}
}

View File

@@ -0,0 +1,40 @@
using MongoDB.Driver;
namespace Core.Blueprint.Mongo
{
/// <summary>
/// Provides the MongoDB provider and database connection using the specified configuration settings.
/// This class manages the connection to MongoDB and ensures that the correct credentials are used for authentication.
/// </summary>
public class MongoProvider
{
private readonly IMongoDatabase _database;
/// <summary>
/// Initializes a new instance of the <see cref="MongoProvider"/> class.
/// This constructor sets up the MongoDB provider using the connection string, audience, and other settings provided.
/// It also configures authentication using OpenID Connect (OIDC) credentials.
/// </summary>
/// <param name="mongoDbSettings">The MongoDB settings required for connecting to the database.</param>
public MongoProvider(IMongoDatabase database)
{
_database = database ?? throw new ArgumentNullException(nameof(database));
}
/// <summary>
/// Gets the initialized MongoDB database. If the database is not initialized, an exception is thrown.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown when the database connection is not initialized.</exception>
protected IMongoDatabase Database
{
get
{
if (_database == null)
{
throw new InvalidOperationException("MongoDB connection is not initialized.");
}
return _database;
}
}
}
}

View File

@@ -0,0 +1,252 @@
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.
/// </summary>
/// <param name="document">The document with the updated data.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
public virtual async Task ReplaceOneAsync(TDocument document)
{
var filter = Builders<TDocument>.Filter.Eq(doc => doc._Id, document._Id);
await _collection.FindOneAndReplaceAsync(filter, document);
}
/// <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));
}
}
}