diff --git a/MongoDbGenericRepository/KeyTypedReadOnlyMongoRepository.cs b/MongoDbGenericRepository/KeyTypedReadOnlyMongoRepository.cs new file mode 100644 index 0000000..3cf82d8 --- /dev/null +++ b/MongoDbGenericRepository/KeyTypedReadOnlyMongoRepository.cs @@ -0,0 +1,331 @@ +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using MongoDbGenericRepository.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; + +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 where TKey : IEquatable + { + + /// + /// The connection string. + /// + public string ConnectionString { get; set; } + + /// + /// The database name. + /// + public string DatabaseName { get; set; } + + /// + /// The MongoDbContext + /// + protected IMongoDbContext MongoDbContext = 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) + { + MongoDbContext = new MongoDbContext(connectionString, databaseName); + } + + #region Read + + /// + /// Asynchronously returns one document given its id. + /// + /// The type representing a Document. + /// The Id of the document you want to get. + /// An optional partition key. + public async Task GetByIdAsync(TKey id, string partitionKey = null) where TDocument : IDocument + { + 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 Id of the document you want to get. + /// An optional partition key. + public TDocument GetById(TKey id, string partitionKey = null) where TDocument : IDocument + { + 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. + /// A LINQ expression filter. + /// An optional partition key. + public async Task GetOneAsync(Expression> filter, string partitionKey = null) where TDocument : IDocument + { + return await HandlePartitioned(partitionKey).Find(filter).FirstOrDefaultAsync(); + } + + /// + /// Returns one document given an expression filter. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// An optional partition key. + public TDocument GetOne(Expression> filter, string partitionKey = null) where TDocument : IDocument + { + return HandlePartitioned(partitionKey).Find(filter).FirstOrDefault(); + } + + /// + /// Returns a collection cursor. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// An optional partition key. + public IFindFluent GetCursor(Expression> filter, string partitionKey = null) where TDocument : IDocument + { + return HandlePartitioned(partitionKey).Find(filter); + } + + /// + /// Returns true if any of the document of the collection matches the filter condition. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// An optional partition key. + public async Task AnyAsync(Expression> filter, string partitionKey = null) where TDocument : IDocument + { + 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. + /// A LINQ expression filter. + /// An optional partition key. + public bool Any(Expression> filter, string partitionKey = null) where TDocument : IDocument + { + 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. + /// A LINQ expression filter. + /// An optional partition key. + public async Task> GetAllAsync(Expression> filter, string partitionKey = null) where TDocument : IDocument + { + return await HandlePartitioned(partitionKey).Find(filter).ToListAsync(); + } + + /// + /// Returns a list of the documents matching the filter condition. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// An optional partition key. + public List GetAll(Expression> filter, string partitionKey = null) where TDocument : IDocument + { + return HandlePartitioned(partitionKey).Find(filter).ToList(); + } + + /// + /// Asynchronously counts how many documents match the filter condition. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// An optional partitionKey + public async Task CountAsync(Expression> filter, string partitionKey = null) where TDocument : IDocument + { + return await HandlePartitioned(partitionKey).CountDocumentsAsync(filter); + } + + /// + /// Counts how many documents match the filter condition. + /// + /// The type representing a Document. + /// A LINQ expression filter. + /// An optional partitionKey + public long Count(Expression> filter, string partitionKey = null) where TDocument : IDocument + { + return HandlePartitioned(partitionKey).Find(filter).CountDocuments(); + } + + /// + /// Gets the document with the maximum value of a specified property in a MongoDB collections that is satisfying the filter. + /// + /// The document type. + /// 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) + where TDocument : IDocument + { + return await 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. + /// A LINQ expression filter. + /// A property selector to order by descending. + /// An optional partitionKey. + /// + public TDocument GetByMax(Expression> filter, Expression> maxValueSelector, string partitionKey = null) + where TDocument : IDocument + { + return GetByMax(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. + /// 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) + where TDocument : IDocument + { + return await GetByMinAsync(filter, minValueSelector, partitionKey); + } + + /// + /// Gets the document with the maximum value of a specified property in a MongoDB collections that is satisfying the filter. + /// + /// The document type. + /// A LINQ expression filter. + /// A property selector to order by ascending. + /// An optional partitionKey. + public TDocument GetByMin(Expression> filter, Expression> orderByAscending, string partitionKey = null) + where TDocument : IDocument + { + return GetByMin(filter, orderByAscending, partitionKey); + } + + /// + /// Gets the maximum value of a property in a mongodb collections that is satisfying the filter. + /// + /// The document type. + /// 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 async Task GetMaxValueAsync(Expression> filter, Expression> maxValueSelector, string partitionKey = null) + where TDocument : IDocument + { + return await 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 value used to order the query. + /// A LINQ expression filter. + /// A property selector to order by ascending. + /// An optional partitionKey. + public TValue GetMaxValue(Expression> filter, Expression> maxValueSelector, string partitionKey = null) + where TDocument : IDocument + { + return 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 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 + { + return GetMinValue(filter, minValueSelector, partitionKey); + } + + #endregion + + #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(); + } + + /// + /// 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 static 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); + } + + #endregion + } +} \ No newline at end of file diff --git a/MongoDbGenericRepository/ReadOnlyMongoRepository.TKey.cs b/MongoDbGenericRepository/ReadOnlyMongoRepository.TKey.cs new file mode 100644 index 0000000..dd6e9e2 --- /dev/null +++ b/MongoDbGenericRepository/ReadOnlyMongoRepository.TKey.cs @@ -0,0 +1,522 @@ +using MongoDB.Driver; +using MongoDB.Driver.Linq; +using MongoDbGenericRepository.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading.Tasks; + +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 partial class ReadOnlyMongoRepository + { + /// + /// The connection string. + /// + public string ConnectionString { get; set; } + + /// + /// The database name. + /// + public string DatabaseName { get; set; } + + /// + /// The MongoDbContext + /// + protected IMongoDbContext MongoDbContext = 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 ReadOnlyMongoRepository(string connectionString, string databaseName) + { + MongoDbContext = new MongoDbContext(connectionString, databaseName); + } + + /// + /// The contructor taking a . + /// + /// A mongodb context implementing + protected ReadOnlyMongoRepository(IMongoDbContext mongoDbContext) + { + MongoDbContext = mongoDbContext; + } + + /// + /// The contructor taking a . + /// + /// A mongodb context implementing + protected ReadOnlyMongoRepository(IMongoDatabase mongoDatabase) + { + MongoDbContext = new MongoDbContext(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 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 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 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 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 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 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 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 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 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 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 long Count(Expression> filter, string partitionKey = null) + where TDocument : IDocument + where TKey : IEquatable + { + return HandlePartitioned(partitionKey).Find(filter).CountDocuments(); + } + + /// + /// 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 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 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 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 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); + } + + /// + /// 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 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 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 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 + { + return await 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 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 + + #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 the type TDocument + /// + /// The document type. + /// 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 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 a potentially partitioned document type. + /// + /// The document type. + /// The collection partition key. + /// + protected virtual IMongoCollection HandlePartitioned(string partitionKey) where TDocument : IDocument + { + if (!string.IsNullOrEmpty(partitionKey)) + { + return GetCollection(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 static 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); + } + + #endregion + } +} \ No newline at end of file