添加dll引用
运行nuget命令:Install-Package mongocsharpdriver
即可安装MongoDb与C#相关的dll,主要有下面这4个dll。
Repository数据访问设计
1,BaseMongoDB
主要封装了连接数据库,获取数据库和获取集合的操作,如下代码。
namespace XXX.Frameworks.MongoDb.Repository { /// <summary> /// MongoDb基类 /// 主要封装了创建数据库连接,获取数据库和获取集合的操作 /// </summary> /// <typeparam name="T"></typeparam> public class BaseMongoDB<T> where T : class { public BaseMongoDB(string connectionString) : this(connectionString, null) { } public BaseMongoDB(string connectionString, string collectionName) { if (connectionString.IsNullOrEmpty()) { connectionString = ConfigurationManager.ConnectionStrings["MongoTicketDB"].ConnectionString; } if (connectionString.IsNullOrEmpty()) { throw new Exception("mongodb connectionString can not be empty!"); } var mongoUrl = new MongoUrl(connectionString); DB = GetDatabaseFromUrl(mongoUrl); if (!string.IsNullOrEmpty(collectionName)) { CollectionName = collectionName; this.Collection = DB.GetCollection<T>(collectionName); } } /// <summary> /// 依据连接字符串获取数据库 /// </summary> /// <param name="url"></param> /// <returns></returns> private IMongoDatabase GetDatabaseFromUrl(MongoUrl url) { var client = new MongoClient(url); var db = client.GetDatabase(url.DatabaseName); return db; } /// <summary> /// 集合 /// </summary> public IMongoCollection<T> Collection { get; } /// <summary> /// 数据库 /// </summary> public IMongoDatabase DB { get; } /// <summary> /// 集合名称,默认为空 /// </summary> public string CollectionName { get; } = string.Empty; } }
2,MongoRepository
主要封装了对数据库的常用操作,比如增,删,改和查等,如下代码。
namespace XXX.Frameworks.MongoDb.Repository { /// <summary> /// MongoDb数据访问基类 /// </summary> /// <typeparam name="T"></typeparam> /// <typeparam name="TKey"></typeparam> public class MongoRepository<T, TKey> : BaseMongoDB<T> where T : class { private readonly Type _keyType; protected MongoRepository(string connectionString = "", string collectionName = "") : base(connectionString, GetCollectionName(collectionName)) { _keyType = typeof(TKey); if (_keyType != typeof(string) && _keyType != typeof(long) && _keyType != typeof(ObjectId)) { throw new Exception("TKey must these type: string or long or ObjectId"); } Initial(); } /// <summary> /// 初始化 /// 注册序列化器 /// </summary> private void Initial() { try { var serializer = BsonSerializer.LookupSerializer(typeof(DateTime)); if (serializer == null || serializer.GetType() != typeof(LocalTimeSerializer)) { // remove exist var cacheFieldInfo = typeof(BsonSerializerRegistry). GetField("_cache", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); var _cacheTemp = cacheFieldInfo.GetValue(BsonSerializer.SerializerRegistry); var _cache = _cacheTemp as ConcurrentDictionary<Type, IBsonSerializer>; IBsonSerializer removeed; _cache.TryRemove(typeof(DateTime), out removeed); // add my owner BsonSerializer.RegisterSerializer(typeof(DateTime), new LocalTimeSerializer()); } } catch { } } /// <summary> /// 依据连接字符串创建数据库访问实例 /// </summary> /// <param name="db"></param> /// <returns></returns> public static MongoRepository<T, TKey> CreateRepository(MongoDBs db = MongoDBs.MongoTicketDB) { var connStrName = Enum.GetName(typeof(MongoDBs), db); var connStr = ConfigurationManager.ConnectionStrings[connStrName].ConnectionString; return new MongoRepository<T, TKey>(connStr); } /// <summary> /// 获取集合名 /// </summary> /// <param name="specifiedCollectionName"></param> /// <returns></returns> private static string GetCollectionName(string specifiedCollectionName) { string collectionName; if (!specifiedCollectionName.IsNullOrEmpty()) { collectionName = specifiedCollectionName; } else { var att = Attribute.GetCustomAttribute(typeof(T), typeof(CollectionNameAttribute)); collectionName = att != null ? ((CollectionNameAttribute)att).Name : typeof(T).Name; if (string.IsNullOrEmpty(collectionName)) { throw new ArgumentException("Collection name cannot be empty for this entity"); } } return collectionName; } /// <summary> /// 判断集合是否存在 /// </summary> /// <param name="predicate"></param> /// <returns></returns> public bool Exists(Expression<Func<T, bool>> predicate) { var count = this.Collection.Find(predicate).CountAsync().Result; return count > 0; } /// <summary> /// 依据id获取实体(文档) /// </summary> /// <param name="id"></param> /// <returns></returns> public T GetById(TKey id) { var filter = Builders<T>.Filter.Eq("_id", id); var result = this.Collection.Find(filter).FirstOrDefaultAsync().Result; return result; } /// <summary> /// 查询所有实体 /// </summary> /// <returns></returns> public IEnumerable<T> GetAll() { return this.Collection.Find(new BsonDocument()).ToListAsync().Result; } /// <summary> /// 依据查询表达式来查询实体 /// </summary> /// <param name="predicate"></param> /// <returns></returns> public IEnumerable<T> GetBy(Expression<Func<T, bool>> predicate) { return this.Collection.Find(predicate).ToListAsync().Result; } /// <summary> /// 分页查询 /// </summary> /// <param name="predicate"></param> /// <param name="pageIndex"></param> /// <param name="pageSize"></param> /// <param name="orderBy"></param> /// <returns></returns> public IEnumerable<T> GetByPaging(Expression<Func<T, bool>> predicate, int pageIndex, int pageSize, IList<OrderBy<T>> orderBy = null) { if (pageIndex < 1) { pageIndex = 1; } if (pageSize < 1) { pageSize = 15; } var data = this.Collection.Find(predicate); if (orderBy != null && orderBy.Count > 0) { var idx = 0; foreach (var sort in orderBy) { idx++; if (idx == 1) { data = sort.IsAscending ? data.SortBy(sort.Field) : data.SortByDescending(sort.Field); } else { var sortByData = data as IOrderedFindFluent<T, T>; if (sortByData == null) { continue; } data = sort.IsAscending ? sortByData.ThenBy(sort.Field) : sortByData.ThenByDescending(sort.Field); } } } var result = data.Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToListAsync().Result; return result; } /// <summary> /// 新增(单个) /// </summary> /// <param name="entity"></param> /// <returns></returns> public void Insert(T entity) { AsyncHelper.RunSync(() => this.Collection.InsertOneAsync(entity)); } /// <summary> /// 新增(批量) /// </summary> /// <param name="entities"></param> /// <returns></returns> public void Insert(IEnumerable<T> entities) { AsyncHelper.RunSync(() => this.Collection.InsertManyAsync(entities)); } /// <summary> /// 新增,从json字符串 /// </summary> /// <param name="json"></param> /// <returns></returns> public IList<BsonDocument> Insert(string json) { if (string.IsNullOrWhiteSpace(json)) { return new List<BsonDocument>(); } if (typeof(T) != typeof(BsonDocument)) { throw new Exception("this method just for MogoRepository<BsonDocument> "); } var documents = new List<BsonDocument>(); if (json.TrimStart().StartsWith("[")) { var bsonDocument = BsonSerializer.Deserialize<BsonArray>(json); foreach (var b in bsonDocument) { var bd = b.ToBsonDocument(); bd.Remove("$id"); documents.Add(bd); } } else { var bsonDocument = BsonSerializer.Deserialize<BsonDocument>(json); bsonDocument.Remove("$id"); documents.Add(bsonDocument); } AsyncHelper.RunSync(()=> this.DB.GetCollection<BsonDocument>(this.CollectionName).InsertManyAsync(documents)); return documents; } /// <summary> /// 更新(单个) /// </summary> /// <param name="entity"></param> /// <returns>返回影响的行数</returns> public long Update(T entity) { var filter = Builders<T>.Filter.Eq("_id", GetIdValue(entity)); var replaceResult = this.Collection.ReplaceOneAsync(filter, entity).Result; return replaceResult.ModifiedCount; } /// <summary> /// 更新(批量) /// </summary> /// <param name="entities"></param> /// <returns></returns> public long Update(IEnumerable<T> entities) { long modifiedCount = 0; foreach (var entity in entities) { var filter = Builders<T>.Filter.Eq("_id", GetIdValue(entity)); var replaceResult = this.Collection.ReplaceOneAsync(filter, entity).Result; modifiedCount += replaceResult.ModifiedCount; } return modifiedCount; } /// <summary> /// 更新,如果不存在则新增 /// </summary> /// <param name="entity"></param> /// <returns></returns> public bool UpdateOrAdd(T entity) { SaveBigDataToLog(entity); if (Update(entity) < 1) { this.Collection.InsertOneAsync(entity); } return true; } /// <summary> /// 更新(局部更新) /// </summary> /// <param name="query"></param> /// <param name="columnValues">指定字段</param> /// <returns></returns> public long Update(Expression<Func<T, bool>> query, Dictionary<string, object> columnValues) { if (columnValues == null || columnValues.Count == 0) { throw new ArgumentException("Update Columns is Null!", nameof(columnValues)); } var fileter = Builders<T>.Filter.Where(query); var update = Builders<T>.Update; UpdateDefinition<T> updateDefinination = null; columnValues.Keys.ToList().ForEach(x => { updateDefinination = updateDefinination == null ? update.Set(x, columnValues[x]) : updateDefinination.Set(x, columnValues[x]); }); var result = this.Collection.UpdateManyAsync<T>(query, updateDefinination).Result; return result.ModifiedCount; } /// <summary> /// 依据id删除数据(文档) /// </summary> /// <param name="id"></param> /// <returns></returns> public long Delete(TKey id) { var filter = Builders<T>.Filter.Eq("_id", id); var result = this.Collection.DeleteOneAsync(filter).Result; return result.DeletedCount; } /// <summary> /// 删除,依据实体 /// </summary> /// <param name="entity"></param> /// <returns></returns> public long Delete(T entity) { var filter = Builders<T>.Filter.Eq("_id", GetIdValue(entity)); var result = this.Collection.DeleteOneAsync(filter).Result; return result.DeletedCount; } /// <summary> /// 删除,依据查询表达式 /// </summary> /// <param name="predicate"></param> /// <returns></returns> public long Delete(Expression<Func<T, bool>> predicate) { var result = this.Collection.DeleteManyAsync<T>(predicate).Result; return result.DeletedCount; } /// <summary> /// 删除所有 /// </summary> /// <returns></returns> public void RemoveAll() { AsyncHelper.RunSync(()=> this.Collection.DeleteManyAsync(new BsonDocument())); } #region Private method /// <summary> /// 获取实体的id /// </summary> /// <param name="entity"></param> /// <returns></returns> private object GetIdValue(T entity) { object result = null; if (typeof(T).IsSubclassOf(typeof(BaseMongoEntity<TKey>))) { var baseEntity = entity as BaseMongoEntity<TKey>; result = baseEntity.Id; } else if (typeof(T) == typeof(BsonDocument)) { var bson = entity as BsonDocument; var objectId = bson["_id"]; if (objectId != null && objectId.IsObjectId) { result = objectId.AsObjectId; } } if (result == null) { throw new Exception("can not get Id (or ObjectId) from the entity, please check!"); } return result; } /// <summary> /// 记录日志 /// </summary> /// <param name="entity"></param> private void SaveBigDataToLog(T entity) { try { var dataJson = entity.ToJson(); var saveDirector = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "VLogs", "PackageFH"); saveDirector = Path.Combine(saveDirector, "BigDataLog"); saveDirector = Path.Combine(saveDirector, DateTime.Now.ToString("yyyyMMdd")); if (!Directory.Exists(saveDirector)) { Directory.CreateDirectory(saveDirector); } var fileName = Path.Combine(saveDirector, $"{typeof(T).Name}_{Guid.NewGuid()}"); File.WriteAllText(fileName, dataJson); } catch { } } #endregion } }
Entity实体设计
因为mongoDb集合的主键主要有string,int和ObjectId(mongodb内置类型)这几种类型,所以封装了包含字段Id的泛型类型,如下代码。
namespace XXX.Frameworks.MongoDb { public interface IEntity<TKey> { TKey Id { get; set; } } }
namespace XXX.Frameworks.MongoDb { [DataContract(IsReference = true)] [Serializable] public abstract class BaseMongoEntity<TKey> : IEntity<TKey> { [DataMember] public TKey Id { get; set; } } }
namespace XXX.Frameworks.MongoDb.Tests.Entity { [DataContract(IsReference = true)] [Serializable] public class OrderLog: BaseMongoEntity<string> { [DataMember] public int OrderId { get; set; } [DataMember] public decimal OrderAmount { get; set; } [DataMember] public string Title { get; set; } [DataMember] public string Summary { get; set; } [DataMember] [BsonDateTimeOptions(Kind = DateTimeKind.Local)] public DateTime? UpdatedDate { get; set; } [DataMember] [BsonDateTimeOptions(Kind = DateTimeKind.Local)] public DateTime OrderDate { get; set; } } }
需要注意的是,一般情形下,集合的主键就指定为默认的ObjectId类型,也可以手动指定为string和int类型。