using Common; using MongoDB.Bson; using MongoDB.Driver; using MongoDB.Driver.Linq; using MongoRepository.Attributes; using MongoRepository.Config; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Logging; namespace MongoRepository { public class MongoBaseRepository : IMongoBaseRepository where TDocument : MongoBaseEntity { public readonly IMongoCollection _collection; private readonly ILogger> _logger; private static readonly HashSet _indexedCollections = new(); public MongoBaseRepository(IMongoDbSettings settings, ILogger> logger) { _logger = logger; var mongoUrl = new MongoUrlBuilder($"{settings.ServerUrl.TrimEnd('/')}") { Username = settings.Username, Password = settings.Password, DatabaseName = GetDataBaseName(typeof(TDocument)) }.ToMongoUrl(); var client = new MongoClient(mongoUrl); EnsureDatabaseAndUser(client, settings, GetDataBaseName(typeof(TDocument))); var database = client.GetDatabase(GetDataBaseName(typeof(TDocument))); _collection = database.GetCollection(GetCollectionName(typeof(TDocument))); EnsureIndexes(database); } private void EnsureIndexes(IMongoDatabase database) { var collectionName = GetCollectionName(typeof(TDocument)); var key = $"{database.DatabaseNamespace.DatabaseName}:{collectionName}"; if (!_indexedCollections.Add(key)) return; try { if (collectionName == "user") { var bsonCollection = database.GetCollection(collectionName); var mobileIndex = new CreateIndexModel( Builders.IndexKeys.Ascending("BaseInfo.Mobile"), new CreateIndexOptions { Unique = true, Sparse = true, Name = "idx_baseinfo_mobile_unique" } ); var idCardIndex = new CreateIndexModel( Builders.IndexKeys.Ascending("RealName.IDCard"), new CreateIndexOptions { Unique = true, Sparse = true, Name = "idx_realname_idcard_unique" } ); bsonCollection.Indexes.CreateMany(new[] { mobileIndex, idCardIndex }); } } catch (Exception ex) { _logger.LogWarning(ex, "MongoDB 索引创建异常(可能已存在): collection={Collection}", collectionName); } } private void EnsureDatabaseAndUser(MongoClient client, IMongoDbSettings settings, string databaseName) { try { var databaseNames = client.ListDatabaseNames().ToList(); if (!databaseNames.Contains(databaseName)) { var tempDatabase = client.GetDatabase(databaseName); var tempCollection = tempDatabase.GetCollection("temp_collection"); var tempDoc = new BsonDocument("temp", "init"); tempCollection.InsertOne(tempDoc); tempCollection.DeleteOne(new BsonDocument("temp", "init")); } } catch (Exception ex) when (ex.GetType().Name == "MongoAuthenticationException") { } catch (Exception ex) { _logger.LogError(ex, "检查MongoDB数据库失败 database={DatabaseName}", databaseName); } } private protected string GetCollectionName(Type documentType) { var collectionName = ((BsonCollectionAttribute)documentType.GetCustomAttributes( typeof(BsonCollectionAttribute), true) .FirstOrDefault())?.CollectionName; var formatStr = Tools.RegexSubstring(collectionName, "{", "}"); if (!string.IsNullOrEmpty(collectionName) && !string.IsNullOrEmpty(formatStr)) { collectionName = collectionName.Replace("{" + formatStr + "}", DateTime.Now.ToString(formatStr)); } return collectionName; } private protected string GetDataBaseName(Type documentType) { return ((BsonCollectionAttribute)documentType.GetCustomAttributes( typeof(BsonCollectionAttribute), true) .FirstOrDefault())?.DataBaseName; } public virtual IQueryable AsQueryable() { return _collection.AsQueryable(); } public virtual IMongoCollection GetCollection() { return _collection; } public virtual IEnumerable FilterBy( Expression> filterExpression) { return _collection.Find(filterExpression).ToEnumerable(); } public virtual async Task> FilterByAsync( Expression> filterExpression) { return await _collection.Find(filterExpression).ToListAsync(); } public virtual async Task> FilterByAsync( FilterDefinition filterExpression) { return await _collection.Find(filterExpression).ToListAsync(); } public virtual IEnumerable FilterBy( FilterDefinition filterExpression) { return _collection.Find(filterExpression).ToEnumerable(); } public virtual IEnumerable FilterBy( Expression> filterExpression, Expression> projectionExpression) { return _collection.Find(filterExpression).Project(projectionExpression).ToEnumerable(); } public virtual TDocument FindOne(FilterDefinition filterExpression) { return _collection.Find(filterExpression).FirstOrDefault(); } public virtual Task FindOneAsync(FilterDefinition filterExpression) { return Task.Run(() => _collection.Find(filterExpression).FirstOrDefaultAsync()); } public virtual TDocument FindOne(Expression> filterExpression) { return _collection.Find(filterExpression).FirstOrDefault(); } public virtual Task FindOneAsync(Expression> filterExpression) { return Task.Run(() => _collection.Find(filterExpression).FirstOrDefaultAsync()); } public virtual TDocument FindOne(FilterDefinition filterExpression, Expression> orderExpression) { return _collection.Find(filterExpression).SortByDescending(orderExpression).FirstOrDefault(); } public virtual Task FindOneAsync(FilterDefinition filterExpression, Expression> orderExpression) { return Task.Run(() => _collection.Find(filterExpression).SortByDescending(orderExpression).FirstOrDefaultAsync()); } public virtual TDocument FindOne(Expression> filterExpression, Expression> orderExpression) { return _collection.Find(filterExpression).SortByDescending(orderExpression).FirstOrDefault(); } public virtual Task FindOneAsync(Expression> filterExpression, Expression> orderExpression) { return Task.Run(() => _collection.Find(filterExpression).SortByDescending(orderExpression).FirstOrDefaultAsync()); } public virtual TDocument FindById(string id) { var filter = Builders.Filter.Eq(doc => doc.Id, id); return _collection.Find(filter).SingleOrDefault(); } public virtual Task FindByIdAsync(string id) { return Task.Run(() => { var filter = Builders.Filter.Eq(doc => doc.Id, id); return _collection.Find(filter).SingleOrDefaultAsync(); }); } public virtual (long count, List list) QueryPage(Expression> filterExpression, Expression> orderExpression, bool desc, int pageSize, int pageIndex) { pageSize = pageSize <= 0 ? 10 : pageSize; pageIndex = pageIndex <= 0 ? 1 : pageIndex; var count = _collection.Find(filterExpression).CountDocuments(); if (count == 0) { return (0, new List()); } var query = _collection.Find(filterExpression); if (desc) { query = query.SortByDescending(orderExpression); } else { query = query.SortBy(orderExpression); } var list = query.Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToList(); return (count, list); } public virtual async Task<(long count, List list)> QueryPageAsync(Expression> filterExpression, int pageSize, int pageIndex) { try { pageSize = pageSize <= 0 ? 10 : pageSize; pageIndex = pageIndex <= 0 ? 1 : pageIndex; var count = await _collection.Find(filterExpression).CountDocumentsAsync(); if (count == 0) { return (0, new List()); } var query = _collection.Find(filterExpression); var list = await query.Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToListAsync(); return (count, list); } catch (Exception ex) { throw ex; } } public virtual async Task<(long count, List list)> QueryPageAsync(Expression> filterExpression, Expression> orderExpression, bool desc, int pageSize, int pageIndex) { try { pageSize = pageSize <= 0 ? 10 : pageSize; pageIndex = pageIndex <= 0 ? 1 : pageIndex; var count = await _collection.Find(filterExpression).CountDocumentsAsync(); if (count == 0) { return (0, new List()); } var query = _collection.Find(filterExpression); if (desc) { query = query.SortByDescending(orderExpression); } else { query = query.SortBy(orderExpression); } var list = await query.Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToListAsync(); return (count, list); } catch (Exception ex) { throw ex; } } public virtual async Task<(long count, List list)> QueryPageAsync(Expression> filterExpression, Expression> orderExpression, bool desc, int pageSize, int pageIndex, int skipCount) { try { pageSize = pageSize <= 0 ? 10 : pageSize; pageIndex = pageIndex <= 0 ? 1 : pageIndex; var count = await _collection.Find(filterExpression).CountDocumentsAsync(); if (count == 0) { return (0, new List()); } var query = _collection.Find(filterExpression); if (desc) { query = query.SortByDescending(orderExpression); } else { query = query.SortBy(orderExpression); } var list = await query.Skip((pageIndex - 1) * pageSize + skipCount).Limit(pageSize).ToListAsync(); return (count - skipCount, list); } catch (Exception ex) { throw ex; } } public virtual async Task<(long count, List list)> QueryPageTwoOrderAsync(Expression> filterExpression, Expression> orderExpression, Expression> thenorderExpression, bool desc, int pageSize, int pageIndex) { try { pageSize = pageSize <= 0 ? 10 : pageSize; pageIndex = pageIndex <= 0 ? 1 : pageIndex; var count = await _collection.Find(filterExpression).CountDocumentsAsync(); if (count == 0) { return (0, new List()); } var query = _collection.Find(filterExpression); if (desc) { query = query.SortByDescending(orderExpression).ThenByDescending(thenorderExpression); } else { query = query.SortBy(orderExpression).ThenByDescending(thenorderExpression); } var list = await query.Skip((pageIndex - 1) * pageSize ).Limit(pageSize).ToListAsync(); return (count , list); } catch (Exception ex) { throw ex; } } public virtual async Task<(long count, List list)> QueryPageAsync( FilterDefinition filterExpression, SortDefinition orderExpression, bool desc, int pageSize, int pageIndex) { try { pageSize = pageSize <= 0 ? 10 : pageSize; pageIndex = pageIndex <= 0 ? 1 : pageIndex; var count = await _collection.Find(filterExpression).CountDocumentsAsync(); if (count == 0) { return (0, new List()); } var query = _collection.Find(filterExpression).Sort(orderExpression); var list = await query.Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToListAsync(); return (count, list); } catch (Exception ex) { throw ex; } } public virtual async Task<(long count, List list)> QueryPageAsync( FilterDefinition filterExpression, SortDefinition orderExpression, bool desc, int pageSize, int pageIndex, int skipIndex) { try { pageSize = pageSize <= 0 ? 10 : pageSize; pageIndex = pageIndex <= 0 ? 1 : pageIndex; var count = await _collection.Find(filterExpression).CountDocumentsAsync(); if (count == 0) { return (0, new List()); } var query = _collection.Find(filterExpression).Sort(orderExpression); var list = await query.Skip((pageIndex - 1) * pageSize + skipIndex).Limit(pageSize).ToListAsync(); return (count - skipIndex, list); } catch (Exception ex) { throw ex; } } public virtual (bool ok, string message) InsertOne(TDocument document) { try { _collection.InsertOne(document); return (true, null); } catch (Exception ex) { return (false, ex.Message); } } public virtual async Task<(bool ok, string message)> InsertOneAsync(TDocument document) { try { await Task.Run(() => _collection.InsertOneAsync(document)); return (true, null); } catch (Exception ex) { return (false, ex.Message); } } public (bool ok, string message) InsertMany(ICollection documents) { try { _collection.InsertMany(documents); return (true, null); } catch (Exception ex) { return (false, ex.Message); } } public virtual async Task<(bool ok, string message)> InsertManyAsync(ICollection documents) { try { await Task.Run(() => _collection.InsertManyAsync(documents)); return (true, null); } catch (Exception ex) { return (false, ex.Message); } } public (bool ok, string message) ReplaceOne(TDocument document) { var filter = Builders.Filter.Eq(doc => doc.Id, document.Id); try { _collection.FindOneAndReplace(filter, document); return (true, null); } catch (Exception ex) { return (false, ex.Message); } } public virtual (bool ok, string message) UpdateById(string id, UpdateDefinition updateDefinition) { var filter = Builders.Filter.Eq(doc => doc.Id, id); var result = _collection.UpdateOne(filter, updateDefinition); return (result.IsAcknowledged, null); } public virtual async Task<(bool ok, string message)> UpdateByIdAsync(string id, UpdateDefinition updateDefinition) { var filter = Builders.Filter.Eq(doc => doc.Id, id); try { var result = await _collection.UpdateOneAsync(filter, updateDefinition); return (result.IsAcknowledged, null); } catch (Exception ex) { return (false, ex.Message); } } public virtual async Task CountAsync( Expression> filterExpression) { return await _collection.Find(filterExpression).CountDocumentsAsync(); } public virtual long Count( Expression> filterExpression) { return _collection.Find(filterExpression).CountDocuments(); } public virtual async Task CountAsync( FilterDefinition filterExpression) { return await _collection.Find(filterExpression).CountDocumentsAsync(); } public virtual long Count( FilterDefinition filterExpression) { return _collection.Find(filterExpression).CountDocuments(); } public virtual (bool ok, string message) UpdateMany(Expression> filterExpression, UpdateDefinition updateDefinition) { var result = _collection.UpdateMany(filterExpression, updateDefinition); return (result.IsAcknowledged, null); } public virtual async Task<(bool ok, string message)> UpdateManyAsync(Expression> filterExpression, UpdateDefinition updateDefinition) { try { var result = await _collection.UpdateManyAsync(filterExpression, updateDefinition); return (result.IsAcknowledged, null); } catch (Exception ex) { return (false, ex.Message); } } public virtual (bool ok, string message) UpdateMany(FilterDefinition filterExpression, UpdateDefinition updateDefinition) { var result = _collection.UpdateMany(filterExpression, updateDefinition); return (result.IsAcknowledged, null); } public virtual async Task<(bool ok, string message)> UpdateManyAsync(FilterDefinition filterExpression, UpdateDefinition updateDefinition) { try { var result = await _collection.UpdateManyAsync(filterExpression, updateDefinition); return (result.IsAcknowledged, null); } catch (Exception ex) { return (false, ex.Message); } } public virtual async Task<(bool ok, string message)> ReplaceOneAsync(TDocument document) { var filter = Builders.Filter.Eq(doc => doc.Id, document.Id); try { await _collection.FindOneAndReplaceAsync(filter, document); return (true, null); } catch (Exception ex) { return (false, ex.Message); } } public (bool ok, string message) DeleteOne(Expression> filterExpression) { try { _collection.FindOneAndDelete(filterExpression); return (true, null); } catch (Exception ex) { return (false, ex.Message); } } public async Task<(bool ok, string message)> DeleteOneAsync(Expression> filterExpression) { try { await Task.Run(() => _collection.FindOneAndDeleteAsync(filterExpression)); return (true, null); } catch (Exception ex) { return (false, ex.Message); } } public (bool ok, string message) DeleteById(string id) { var filter = Builders.Filter.Eq(doc => doc.Id, id); try { _collection.FindOneAndDelete(filter); return (true, null); } catch (Exception ex) { return (false, ex.Message); } } public async Task<(bool ok, string message)> DeleteByIdAsync(string id) { var filter = Builders.Filter.Eq(doc => doc.Id, id); try { var deleted = await _collection.FindOneAndDeleteAsync(filter); if (deleted == null) return (false, "记录不存在或已删除"); return (true, null); } catch (Exception ex) { return (false, ex.Message); } } public (bool ok, string message) DeleteMany(Expression> filterExpression) { try { _collection.DeleteMany(filterExpression); return (true, null); } catch (Exception ex) { return (false, ex.Message); } } public async Task<(bool ok, string message)> DeleteManyAsync(Expression> filterExpression) { try { await _collection.DeleteManyAsync(filterExpression); return (true, null); } catch (Exception ex) { return (false, ex.Message); } } public async Task> Aggregate(FilterDefinition filterExpression, ProjectionDefinition group) { try { var res = await Task.Run(() => _collection.Aggregate().Match(filterExpression).Group(group).ToList()); return res; } catch (Exception ex) { return null; } } public async Task> RandomData(Expression> filterExpression, int count) { try { var res = await Task.Run(() => _collection.AsQueryable().Where(filterExpression).Sample(count).ToList()); return res; } catch (Exception ex) { return null; } } } }