C#基于Mongo的官方驅(qū)動手擼一個Super簡易版MongoDB-ORM框架
如題,在GitHub上找了一圈想找一個MongoDB的的ORM框架,未償所愿,就去翻了翻官網(wǎng)(https://docs.mongodb.com/drivers/csharp/)
看了看文檔發(fā)現(xiàn)官方的驅(qū)動功能已經(jīng)相當強大了并且更新速度很快

2.3之后得驅(qū)動版本已經(jīng)支持 .Net 5,而且方法都已支持Task ,可以配合async , await.使用 ,同時也支持Lambda表達式及表達式樹 官方是這么說的(https://mongodb.github.io/mongo-csharp-driver/2.12/what_is_new/)

官方得驅(qū)動如此強大了,還找什么ORM框架,我們自己基于官方驅(qū)動手擼一個簡易版的,首先簡單講一下設(shè)計思路
要求1:首先要有一個對象實體基類,為什么要創(chuàng)建實體對象基類?是因為官方驅(qū)動支持的實體類與Collection得映射 必須要有id字段,對應(yīng)數(shù)據(jù)庫中得"_id",并且這個字段是ObjectIDl類型,像這樣
public class Person
{
[BsonId]
[BsonElement("_id")]
public ObjectId ID { get; set; }
}
所以創(chuàng)建實體基類是為了免去每個實體類都要創(chuàng)建這個id的冗余代碼.
要求2:實現(xiàn)實體類與Collection得自動映射 自動創(chuàng)建數(shù)據(jù)庫連接.這一部分實現(xiàn)就稍微復(fù)雜一些,首先我們需要自定義一個Attribute,用于獲取獲取集合名稱,然后創(chuàng)建一個管理器實現(xiàn)一些自動映射的初始化操作
要求3:實現(xiàn)Repository倉儲類.提供簡單得CRUD方法. 這一部分就比較簡單了,通過封裝直接調(diào)用官方的驅(qū)動提供的API,實現(xiàn)CURD操作
開始實現(xiàn)之前記得添加一下官方的驅(qū)動包直接在Nuget搜索MongoDB.Driver 安裝就可以了 ,我這里使用的是2.12.3版本
第一步:創(chuàng)建對象實體基類
[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 這個結(jié)構(gòu)體是不能序列化的,所以使用 [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>
{
}
第二步:實現(xiàn)實體類與Collection的自動映射;
我們需要先創(chuàng)建一個Attribute類,用于標記實體類來獲取實體類對應(yīng)的集合名稱,如下:
[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名稱
}
接下來實現(xiàn)一個管理器,用于自動映射,數(shù)據(jù)庫連接的自動映射,官方驅(qū)動其實已經(jīng)提供了實體類的自動映射,我們只需要接著稍微封裝一下,官方自動映射demo如下:

有一部分準備工作要做,那就是需要在配置文件添加一個數(shù)據(jù)庫連接的配置,用于連接數(shù)據(jù)庫;

接下實現(xiàn)我們的管理器,這一部分是核心,實現(xiàn)了類與數(shù)據(jù)庫Collection的自動映射,并自動創(chuàng)建出了mongo連接
internal static class GlobleManage<T>
{
private static string _tableName;
private static string _dateBaseName;
private static string _mongoServerSettings;
private static IMongoCollection<T> _mongoCollection;
public static IMongoCollection<T> MongoCollection
{
get => _mongoCollection;
}
public static string DateBaseName
{
get => _dateBaseName;
}
public static string MongoServerSettings
{
get => _mongoServerSettings;
}
public static string TableName
{
get => _tableName;
}
static GlobleManage()
{
Init();
}
private static void Init()
{
//初始化連接字符串
string[] parm = ConfigurationManager.ConnectionStrings["MongoServerSettings"].ConnectionString.Split('/');
_dateBaseName = parm.Last();
_mongoServerSettings = ConfigurationManager.ConnectionStrings["MongoServerSettings"].ConnectionString.Replace(@"/" + _dateBaseName, ":27017");
//根據(jù)實體類標注好的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;
}
//添加實體類映射
BsonClassMap.RegisterClassMap<T>(cm => cm.AutoMap());
_mongoCollection = new MongoClient(_mongoServerSettings).GetDatabase(_dateBaseName).GetCollection<T>(_tableName);
}
}
第三步:實現(xiàn)Repository倉儲類.提供簡單的CRUD方法
首先,先創(chuàng)建倉儲類的泛型接口
public interface IRepository<T> where T : IMongoEntityBase<string>
{
IMongoCollection<T> Collection { get; }
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);
泛型倉儲類實現(xiàn)接口,通過管理器獲取自動映射得到的 IMongoCollection
public class Repository<T> : IRepository<T> where T : IMongoEntityBase<string>
{
private IMongoCollection<T> _mongoCollection = GlobleManage<T>.MongoCollection;
public IMongoCollection<T> Collection => _mongoCollection;
public bool Add(T entity)
{
try
{
_mongoCollection.InsertOne(entity);
return true;
}
catch (Exception)
{
throw;
}
}
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)
{
throw;
}
}
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)
{
throw;
}
}
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)
{
throw;
}
}
}
簡易版的ORM框架就算是基本完成,接下來使用這個框架完成一些CRUD操作
首先,創(chuàng)建一個實體類,并且繼承 MongoEntityBase
[Serializable]
public class Person : MongoEntityBase
{
[BsonConstructor]
public Person(string name, int age, string guid, EnumGender gender)
{
Name = name;
Age = age;
Guid = guid;
Gender = gender;
}
public string Name { get; set; }
public int Age { get; set; }
public string Guid { get; set; }
public EnumGender Gender { get; set; }
public List<Person> Students { get => students; set => students = value; }
public Pet Pet { get => pet; set => pet = value; }
private Pet pet;
public override string ToString()
{
return "DB_ID:" + this.DB_ID + " " + "user:" + Name + " " + "age:" + Age + " " + "guid:" + Guid + " " + "Gender:" + Gender.ToString() + " " + "寵物叫" + Pet.Name + "," + Pet.Age + "歲了";
}
private List<Person> students;
}
public enum EnumGender
{
男,
女
}
public class Pet
{
private string name;
private int age;
public string Name { get => name; set => name = value; }
public int Age { get => age; set => age = value; }
}
然后創(chuàng)建一個窗體 測試一下我們的CRUD功能,調(diào)用很簡單 只需要一句 IRepository<Person> _IRepository = new Repository<Person>();
public partial class Form1 : Form
{
private IRepository<Person> _IRepository = new Repository<Person>();
private Random random = new Random();
public Form1()
{
InitializeComponent();
}
//ADD
private void button1_Click(object sender, EventArgs e)
{
Person person = new Person("張三", 8, Guid.NewGuid().ToString(), EnumGender.男);
person.Students = new List<Person>() { new Person("張小三1", 8, Guid.NewGuid().ToString(), EnumGender.男),
new Person("張小三2", 8, Guid.NewGuid().ToString(), EnumGender.男)
,new Person("張小三3", 8, Guid.NewGuid().ToString(), EnumGender.男)
,new Person("張小三4", 8, Guid.NewGuid().ToString(), EnumGender.男)};
person.Pet = new Pet() { Name = "旺財", Age = 3 };
_IRepository.Add(person);
richTextBox1.Text += "添加成功!\r\n";
}
//Find
private void button2_Click(object sender, EventArgs e)
{
var id = textBox1.Text.Trim();
var list = _IRepository.Find(t => t.DB_ID.Equals(id));
richTextBox1.Text += "Find成功:" + "\r\n ";
foreach (var item in list)
{
richTextBox1.Text += item.ToString() + "\r\n ";
}
}
//Delete
private void button3_Click(object sender, EventArgs e)
{
var id = textBox1.Text.Trim();
//var res = _IRepository.Delete(t => t.DB_ID.Equals(id));
var rese = _IRepository.Find(t => t.DB_ID.Equals(id)).FirstOrDefault();
var res = _IRepository.Delete(rese);
richTextBox1.Text += id + "刪除:" + res;/*res.IsAcknowledged + res.DeletedCount;*/
}
//Update
private void button4_Click(object sender, EventArgs e)
{
var guid = textBox1.Text.Trim();
Person person = _IRepository.Find(t => t.DB_ID.Equals(guid)).FirstOrDefault();
person.Name = "改過之后的名字" + random.Next(1, 10);
var res = _IRepository.Update(person);
richTextBox1.Text += guid + "更新:" + res;
}
//Clear
private void button5_Click(object sender, EventArgs e)
{
textBox1.Clear();
richTextBox1.Clear();
}
//FindAll
private void button6_Click(object sender, EventArgs e)
{
var list = _IRepository.Find();
richTextBox1.Text += "FindAll成功:" + "\r\n ";
foreach (var item in list)
{
richTextBox1.Text += item.ToString() + "\r\n";
}
}
}

簡易版本的功能基本都實現(xiàn),實際上,一個成熟的ORM框架還有好多工作要做
鏈接: https://pan.baidu.com/s/1t9xbfQXhb6iz5QJeC0WLOQ
提取碼: y9d2
以上就是C#基于Mongo的官方驅(qū)動手擼一個Super簡易版MongoDB-ORM框架的詳細內(nèi)容,更多關(guān)于C# MongoDB-ORM框架的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#中csv文件與DataTable互相導(dǎo)入處理實例解析
這篇文章主要介紹了C#中csv文件與DataTable互相導(dǎo)入處理實例解析,非常實用的功能,需要的朋友可以參考下2014-08-08
C#結(jié)合JS修改解決KindEditor彈出層問題
KindEditor 是一款出色的富文本HTML在線編輯器,這里我們講述在使用中遇到的一個問題,在部署到某些 WEB 應(yīng)用項目中,點擊類似彈出層功能時,只顯示了遮罩層,而內(nèi)容層則定位無法正確顯示,所以本文給大家介紹了C#結(jié)合JS 修改解決 KindEditor 彈出層問題2024-06-06
C# byte數(shù)組與Image相互轉(zhuǎn)換的方法
這篇文章介紹了C# byte數(shù)組與Image相互轉(zhuǎn)換的方法,有需要的朋友可以參考一下2013-10-10
c#不使用系統(tǒng)api實現(xiàn)可以指定區(qū)域屏幕截屏功能
這篇文章主要介紹了不使用系統(tǒng)API通過純c#實現(xiàn)屏幕指定區(qū)域截屏功能,截屏后還可以保存圖象文件,大家參考使用吧2014-01-01
淺談Visual Studio 2019 Vue項目的目錄結(jié)構(gòu)
這篇文章主要介紹了Visual Studio 2019 Vue項目 目錄結(jié)構(gòu),本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-03-03

