2023-03-05
数据库
00
请注意,本文编写于 417 天前,最后修改于 404 天前,其中某些信息可能已经过时。

目录

设计思路
实现实体类与Collection的自动映射
实现Repository仓储类.提供简单的CRUD方法
示例代码
创建一个实体类,并且继承 MongoEntityBase
使用

因新项目框架升级为 .Net 5.0,原Framework的MongoDB的ORM,不再适用,且旧ORM使用不便,写查询还需要编写BosnDocument,不易于理解,便有了更新ORM的想法。

于是翻看了一下MongoDB的官方文档,发现官方驱动功能本身已经非常强大,且更新迅速,从2.3版本之后就已经支持 .Net 5.0,方法都已支持Task ,可以配合async , await.使用 ,同时也支持Lambda表达式及表达式树。所以便有了创建一个简易版基于MongoDB官方驱动的ORM的想法。

1677984339841.jpg

设计思路

  1. 对象实体基类

    为什么要创建实体对象基类?是因为官方驱动支持的实体类与Collection得映射,必须要有id字段,对应数据库中得"_id",并且这个字段是ObjectIDl类型,像这样:

csharp
public class Person { [BsonId] [BsonElement("_id")] public ObjectId ID { get; set; } }
  1. 实现实体类与Collection得自动映射 自动创建数据库连接

    我们需要自定义一个Attribute,用于获取获取集合名称,然后创建一个管理器实现一些自动映射的初始化操作;

  2. 实现Repository仓储类.提供简单得CRUD方法

    通过封装直接调用官方的驱动提供的API,实现CURD操作;

具体实现

创建对象实体基类

csharp
[DataContract] [Serializable] [BsonIgnoreExtraElements(Inherited = true)] //当BSON文档被反序列化时,每个元素的名称用于在类映射中查找匹配的成员。通常,如果没有找到匹配的成员,将抛出异常。如果要在反序列化期间忽略其他元素 使用这个特性 public abstract class MongoEntityBase : IMongoEntityBase<string> { protected MongoEntityBase() { DB_ID = ObjectId.GenerateNewId().ToString(); //对id进行初始化 } [DataMember]      [BsonElement("_id")] [BsonRepresentation(BsonType.ObjectId)] //因为 ObjectId 这个结构体是不能序列化的,所以使用 [BsonRepresentation(BsonType.ObjectId)] 标记为这个字符串ID在mongo中代表ObjectId public virtual string DB_ID { get; set; } } public interface IMongoEntityBase<TKey> { [BsonId] TKey DB_ID { get; set; } } public interface IMongoEntityBase : IMongoEntityBase<string> { }

实现实体类与Collection的自动映射

  1.  我们需要先创建一个Attribute类,用于标记实体类来获取实体类对应的集合名称,如下:
csharp
[AttributeUsage(AttributeTargets.Class, Inherited = true)] public class CollectionNameAttribute : Attribute { public CollectionNameAttribute(string name) { if (string.IsNullOrEmpty(name)) throw new ArgumentException("Empty collectionname not allowed", "name"); this.Name = name; } public string Name { get; private set; } //定义一个属性 用于获取Collection名称 }
  1. App.Config中添加配置信息,用于连接数据库

xml
<?xml version="1.0" encoding="utf-8" ?> <configuration> <connectionStrings> <add name="MongoServerSettings" connectionString="mongodb://localhost/Person" /> </connectionStrings> </configuration>
  1. 实现一个管理器,用于自动映射

Tip

数据库连接的自动映射,官方驱动其实已经提供了实体类的自动映射

1630815-20210514153056023-1703034695.png

csharp
internal class GlobleManage<T> { public readonly static GlobleManage<T> Instance = new GlobleManage<T>(); private string _tableName; private string _dateBaseName; private string _mongoServerSettings; private IMongoCollection<T> _mongoCollection; public IMongoCollection<T> MongoCollection { get => _mongoCollection; } public string DateBaseName { get => _dateBaseName; } public string MongoServerSettings { get => _mongoServerSettings; } public string TableName { get => _tableName; set => _tableName = value; } internal GlobleManage() { Init(); } private void Init() { try { //初始化连接字符串 string[] parm = ConfigurationManager.ConnectionStrings["MongoServerSettings"].ConnectionString.Split('/'); _dateBaseName = parm.Last(); _mongoServerSettings = ConfigurationManager.ConnectionStrings["MongoServerSettings"].ConnectionString.Replace(@"/" + _dateBaseName, ":27017"); //根据实体类标注好的Attribute获取表名 var entitytype = typeof(T); var attr = Attribute.GetCustomAttribute(entitytype, typeof(CollectionNameAttribute)); //若Attribute不为空 获取标注的表名 if (attr != null) { _tableName = ((CollectionNameAttribute)attr).Name; } else { //否则 如果类型是MongoEntityBase的派生类 获取类名作为表名 if (typeof(MongoEntityBase).IsAssignableFrom(entitytype)) { // No attribute found, get the basetype while (!entitytype.BaseType.Equals(typeof(MongoEntityBase))) { entitytype = entitytype.BaseType; } } _tableName = entitytype.Name; } //添加实体类映射 if(!BsonClassMap.IsClassMapRegistered(entitytype)) BsonClassMap.RegisterClassMap<T>(cm => cm.AutoMap()); _mongoCollection = new MongoClient(_mongoServerSettings).GetDatabase(_dateBaseName).GetCollection<T>(_tableName); } catch (Exception ex) { _mongoCollection = default; } } public IMongoCollection<T> GetConnection(string tableStr) { return new MongoClient(_mongoServerSettings).GetDatabase(_dateBaseName).GetCollection<T>(tableStr); } }

实现Repository仓储类.提供简单的CRUD方法

  1. 创建仓储类的泛型接口 

csharp
public interface IRepository<T> where T : IMongoEntityBase<string> { IMongoCollection<T> Collection { get; } event Action<object, Exception> ExecutedExceptionEventHandler; bool Add(T entity); bool Delete(T delete, Expression<Func<T, bool>> conditions = null); bool Update(T update, Expression<Func<T, bool>> conditions = null); List<T> Find(Expression<Func<T, bool>> conditions = null); }
  1. 泛型仓储类实现接口,通过管理器获取自动映射得到的 IMongoCollection
csharp
public class Repository<T> : IRepository<T> where T : IMongoEntityBase<string> { private readonly IMongoCollection<T> _mongoCollection; public IMongoCollection<T> Collection => _mongoCollection; public event Action<object, Exception> ExecutedExceptionEventHandler; public Repository() { _mongoCollection = GlobleManage<T>.Instance.MongoCollection; } public Repository(string tableStr) { _mongoCollection = GlobleManage<T>.Instance.GetConnection(tableStr); } public bool Add(T entity) { try { _mongoCollection.InsertOne(entity); return true; } catch (Exception ex) { ExecutedExceptionEventHandler?.Invoke($"ExecutedException Add({typeof(T).FullName})", ex); throw ex; } } public bool Delete(T delete, Expression<Func<T, bool>> conditions = null) { try { string _id = string.Empty; if (conditions == null) { foreach (var item in delete.GetType().GetProperties()) { if (item.Name == "DB_ID" && item.GetValue(delete) != null) { _id = item.GetValue(delete).ToString(); var result = _mongoCollection.DeleteOne(new BsonDocument("_id", BsonValue.Create(new ObjectId(_id)))); return result.IsAcknowledged; } } } var res = _mongoCollection.DeleteOne(conditions); return res.IsAcknowledged; } catch (Exception ex) { ExecutedExceptionEventHandler?.Invoke($"ExecutedException Delete({typeof(T).FullName})", ex); throw ex; } } public bool Update(T update, Expression<Func<T, bool>> conditions = null) { try { ObjectId _id; var options = new ReplaceOptions() { IsUpsert = true }; if (conditions == null) { foreach (var item in update?.GetType().GetProperties()) { if (item.Name == "DB_ID" && item.GetValue(update) != null) { _id = new ObjectId(item.GetValue(update).ToString()); var result = _mongoCollection.ReplaceOne(new BsonDocument("_id", BsonValue.Create(_id)), update, options); return result.IsAcknowledged; } } } var res = _mongoCollection.ReplaceOne(conditions, update, options); return res.IsAcknowledged; } catch (Exception ex) { ExecutedExceptionEventHandler?.Invoke($"ExecutedException Update({typeof(T).FullName},{conditions})", ex); throw ex; } } public List<T> Find(Expression<Func<T, bool>> conditions = null) { try { if (conditions == null) { conditions = t => true; } return _mongoCollection.Find(conditions).ToList() ?? new List<T>(); } catch (Exception ex) { ExecutedExceptionEventHandler?.Invoke($"ExecutedException Find({typeof(T).FullName},{conditions})", ex); throw ex; } } }

至此,一个简易版基于MongoDB官方驱动的ORM就完成了。

示例代码

核心调用:

IRepository<Person> _IRepository = new Repository<Person>();

创建一个实体类,并且继承 MongoEntityBase

csharp
[Serializable] public class Person : MongoEntityBase { [BsonConstructor] public Person(string name, int age, EnumGender gender) { Name = name; Age = age; Gender = gender; } } public enum EnumGender { 男, 女 }

使用

csharp
static void Main(string[] args) { IRepository<Person> _IRepository = new Repository<Person>() //...do something Person person = new Person("张三", 8, EnumGender.男); _IRepository.Add(person); //...do something var personList = _IRepository.Find(t => t.Name.Equals("张三")); //...do something var p = _IRepository.Find(t => t.Name.Equals("张三")).FirstOrDefault(); if(p != null) { p.Name = "李四"; var res = _IRepository.Update(p); } //...do something var p1 = _IRepository.Find(t => t.Name.Equals("李四")).FirstOrDefault(); if(p1 != null) { var res = _IRepository.Delete(p1); } }

本文作者:Peter.Pan

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!