using MongoDB.Driver; using System; using System.Collections.Generic; using System.Threading.Tasks; using System.Linq.Expressions; using MongoDbGenericRepository.Models; using System.Linq; namespace MongoDbGenericRepository { /// /// The ReadOnlyMongoRepository implements the readonly functionality of the IReadOnlyMongoRepository. /// public class ReadOnlyMongoRepository : IReadOnlyMongoRepository { /// /// 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 /// /// 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(Guid 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(Guid 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> orderByDescending, string partitionKey = null) where TDocument : IDocument { return await GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) .SortByDescending(orderByDescending) .FirstOrDefaultAsync(); } /// /// 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> orderByDescending, string partitionKey = null) where TDocument : IDocument { return GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) .SortByDescending(orderByDescending) .FirstOrDefault(); } /// /// 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> orderByAscending, string partitionKey = null) where TDocument : IDocument { return await GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) .SortBy(orderByAscending) .FirstOrDefaultAsync(); } /// /// 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 GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) .SortBy(orderByAscending) .FirstOrDefault(); } #endregion #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> orderByDescending, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { return await GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) .SortByDescending(orderByDescending) .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> orderByDescending, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { return GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) .SortByDescending(orderByDescending) .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> orderByAscending, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { return await GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) .SortBy(orderByAscending) .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> orderByAscending, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { return GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) .SortBy(orderByAscending) .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 Task GetMaxValueAsync(Expression> filter, Expression> maxValueSelector, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { return await GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) .SortByDescending(ConvertExpression(maxValueSelector)) .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. /// A LINQ expression filter. /// A property selector to order by ascending. /// An optional partitionKey. public TValue GetMaxValue(Expression> filter, Expression> orderByDescending, string partitionKey = null) where TDocument : IDocument where TKey : IEquatable { return GetCollection(partitionKey).Find(Builders.Filter.Where(filter)) .SortByDescending(ConvertExpression(orderByDescending)) .Project(orderByDescending) .FirstOrDefault(); } #endregion #region Utility Methods /// /// Gets a collections for the type TDocument with the matching partition key. /// /// The document type. /// The partion key. /// An protected IMongoCollection GetCollection(string partitionKey) where TDocument : IDocument { return MongoDbContext.GetCollection(partitionKey); } /// /// Gets a collections for the type TDocument /// /// The document type. /// protected IMongoCollection GetCollection() where TDocument : IDocument { return MongoDbContext.GetCollection(); } /// /// Gets a collections for the type TDocument /// /// The document type. /// The document. /// protected 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 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 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 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 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 } }