因新项目框架升级为 .Net 5.0,原Framework的MongoDB的ORM,不再适用,且旧ORM使用不便,写查询还需要编写BosnDocument,不易于理解,便有了更新ORM的想法。
于是翻看了一下MongoDB的官方文档,发现官方驱动功能本身已经非常强大,且更新迅速,从2.3版本之后就已经支持 .Net 5.0,方法都已支持Task ,可以配合async , await.使用 ,同时也支持Lambda表达式及表达式树。所以便有了创建一个简易版基于MongoDB官方驱动的ORM的想法。
1677984339841.jpg
设计思路
- 对象实体基类 为什么要创建实体对象基类?是因为官方驱动支持的实体类与Collection得映射,必须要有id字段,对应数据库中得"_id",并且这个字段是ObjectIDl类型,像这样:
public class Person
{
[BsonId]
[BsonElement("_id")]
public ObjectId ID { get; set; }
}
- 实现实体类与Collection得自动映射 自动创建数据库连接 我们需要自定义一个Attribute,用于获取获取集合名称,然后创建一个管理器实现一些自动映射的初始化操作;
- 实现Repository仓储类.提供简单得CRUD方法 通过封装直接调用官方的驱动提供的API,实现CURD操作;
具体实现
创建对象实体基类
代码语言:javascript复制 [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的自动映射
- 我们需要先创建一个Attribute类,用于标记实体类来获取实体类对应的集合名称,如下:
[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名称
}
- App.Config中添加配置信息,用于连接数据库
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="MongoServerSettings" connectionString="mongodb://localhost/Person" />
</connectionStrings>
</configuration>
- 实现一个管理器,用于自动映射
:::tip{title="Tip"} 数据库连接的自动映射,官方驱动其实已经提供了实体类的自动映射
:::
代码语言:javascript复制 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方法
- 创建仓储类的泛型接口
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);
}
- 泛型仓储类实现接口,通过管理器获取自动映射得到的 IMongoCollection
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就完成了。
示例代码
核心调用:
代码语言:javascript复制 IRepository<Person> _IRepository = new Repository<Person>();
创建一个实体类,并且继承 MongoEntityBase
代码语言:javascript复制 [Serializable]
public class Person : MongoEntityBase
{
[BsonConstructor]
public Person(string name, int age, EnumGender gender)
{
Name = name;
Age = age;
Gender = gender;
}
}
public enum EnumGender
{
男,
女
}
使用
代码语言:javascript复制 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);
}
}