C#基于Mongo的官方驅(qū)動(dòng)手?jǐn)]一個(gè)Super簡(jiǎn)易版MongoDB-ORM框架
如題,在GitHub上找了一圈想找一個(gè)MongoDB的的ORM框架,未償所愿,就去翻了翻官網(wǎng)(https://docs.mongodb.com/drivers/csharp/)
看了看文檔發(fā)現(xiàn)官方的驅(qū)動(dòng)功能已經(jīng)相當(dāng)強(qiáng)大了并且更新速度很快
2.3之后得驅(qū)動(dòng)版本已經(jīng)支持 .Net 5,而且方法都已支持Task ,可以配合async , await.使用 ,同時(shí)也支持Lambda表達(dá)式及表達(dá)式樹(shù) 官方是這么說(shuō)的(https://mongodb.github.io/mongo-csharp-driver/2.12/what_is_new/)
官方得驅(qū)動(dòng)如此強(qiáng)大了,還找什么ORM框架,我們自己基于官方驅(qū)動(dòng)手?jǐn)]一個(gè)簡(jiǎn)易版的,首先簡(jiǎn)單講一下設(shè)計(jì)思路
要求1:首先要有一個(gè)對(duì)象實(shí)體基類(lèi),為什么要?jiǎng)?chuàng)建實(shí)體對(duì)象基類(lèi)?是因?yàn)楣俜津?qū)動(dòng)支持的實(shí)體類(lèi)與Collection得映射 必須要有id字段,對(duì)應(yīng)數(shù)據(jù)庫(kù)中得"_id",并且這個(gè)字段是ObjectIDl類(lèi)型,像這樣
public class Person { [BsonId] [BsonElement("_id")] public ObjectId ID { get; set; } }
所以創(chuàng)建實(shí)體基類(lèi)是為了免去每個(gè)實(shí)體類(lèi)都要?jiǎng)?chuàng)建這個(gè)id的冗余代碼.
要求2:實(shí)現(xiàn)實(shí)體類(lèi)與Collection得自動(dòng)映射 自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù)連接.這一部分實(shí)現(xiàn)就稍微復(fù)雜一些,首先我們需要自定義一個(gè)Attribute,用于獲取獲取集合名稱(chēng),然后創(chuàng)建一個(gè)管理器實(shí)現(xiàn)一些自動(dòng)映射的初始化操作
要求3:實(shí)現(xiàn)Repository倉(cāng)儲(chǔ)類(lèi).提供簡(jiǎn)單得CRUD方法. 這一部分就比較簡(jiǎn)單了,通過(guò)封裝直接調(diào)用官方的驅(qū)動(dòng)提供的API,實(shí)現(xiàn)CURD操作
開(kāi)始實(shí)現(xiàn)之前記得添加一下官方的驅(qū)動(dòng)包直接在Nuget搜索MongoDB.Driver 安裝就可以了 ,我這里使用的是2.12.3版本
第一步:創(chuàng)建對(duì)象實(shí)體基類(lèi)
[DataContract] [Serializable] [BsonIgnoreExtraElements(Inherited = true)] //當(dāng)BSON文檔被反序列化時(shí),每個(gè)元素的名稱(chēng)用于在類(lèi)映射中查找匹配的成員。通常,如果沒(méi)有找到匹配的成員,將拋出異常。如果要在反序列化期間忽略其他元素 使用這個(gè)特性 public abstract class MongoEntityBase : IMongoEntityBase<string> { protected MongoEntityBase() { DB_ID = ObjectId.GenerateNewId().ToString(); //對(duì)id進(jìn)行初始化 } [DataMember] [BsonElement("_id")] [BsonRepresentation(BsonType.ObjectId)] //因?yàn)?ObjectId 這個(gè)結(jié)構(gòu)體是不能序列化的,所以使用 [BsonRepresentation(BsonType.ObjectId)] 標(biāo)記為這個(gè)字符串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> { }
第二步:實(shí)現(xiàn)實(shí)體類(lèi)與Collection的自動(dòng)映射;
我們需要先創(chuàng)建一個(gè)Attribute類(lèi),用于標(biāo)記實(shí)體類(lèi)來(lái)獲取實(shí)體類(lèi)對(duì)應(yīng)的集合名稱(chē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; } //定義一個(gè)屬性 用于獲取Collection名稱(chēng) }
接下來(lái)實(shí)現(xiàn)一個(gè)管理器,用于自動(dòng)映射,數(shù)據(jù)庫(kù)連接的自動(dòng)映射,官方驅(qū)動(dòng)其實(shí)已經(jīng)提供了實(shí)體類(lèi)的自動(dòng)映射,我們只需要接著稍微封裝一下,官方自動(dòng)映射demo如下:
有一部分準(zhǔn)備工作要做,那就是需要在配置文件添加一個(gè)數(shù)據(jù)庫(kù)連接的配置,用于連接數(shù)據(jù)庫(kù);
接下實(shí)現(xiàn)我們的管理器,這一部分是核心,實(shí)現(xiàn)了類(lèi)與數(shù)據(jù)庫(kù)Collection的自動(dòng)映射,并自動(dòng)創(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ù)實(shí)體類(lèi)標(biāo)注好的Attribute獲取表名 var entitytype = typeof(T); var attr = Attribute.GetCustomAttribute(entitytype, typeof(CollectionNameAttribute)); //若Attribute不為空 獲取標(biāo)注的表名 if (attr != null) { _tableName = ((CollectionNameAttribute)attr).Name; } else { //否則 如果類(lèi)型是MongoEntityBase的派生類(lèi) 獲取類(lèi)名作為表名 if (typeof(MongoEntityBase).IsAssignableFrom(entitytype)) { // No attribute found, get the basetype while (!entitytype.BaseType.Equals(typeof(MongoEntityBase))) { entitytype = entitytype.BaseType; } } _tableName = entitytype.Name; } //添加實(shí)體類(lèi)映射 BsonClassMap.RegisterClassMap<T>(cm => cm.AutoMap()); _mongoCollection = new MongoClient(_mongoServerSettings).GetDatabase(_dateBaseName).GetCollection<T>(_tableName); } }
第三步:實(shí)現(xiàn)Repository倉(cāng)儲(chǔ)類(lèi).提供簡(jiǎn)單的CRUD方法
首先,先創(chuàng)建倉(cāng)儲(chǔ)類(lèi)的泛型接口
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);
泛型倉(cāng)儲(chǔ)類(lèi)實(shí)現(xiàn)接口,通過(guò)管理器獲取自動(dòng)映射得到的 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; } } }
簡(jiǎn)易版的ORM框架就算是基本完成,接下來(lái)使用這個(gè)框架完成一些CRUD操作
首先,創(chuàng)建一個(gè)實(shí)體類(lèi),并且繼承 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)建一個(gè)窗體 測(cè)試一下我們的CRUD功能,調(diào)用很簡(jiǎn)單 只需要一句 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 = "旺財(cái)", 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 = "改過(guò)之后的名字" + 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"; } } }
簡(jiǎn)易版本的功能基本都實(shí)現(xiàn),實(shí)際上,一個(gè)成熟的ORM框架還有好多工作要做
鏈接: https://pan.baidu.com/s/1t9xbfQXhb6iz5QJeC0WLOQ
提取碼: y9d2
以上就是C#基于Mongo的官方驅(qū)動(dòng)手?jǐn)]一個(gè)Super簡(jiǎn)易版MongoDB-ORM框架的詳細(xì)內(nèi)容,更多關(guān)于C# MongoDB-ORM框架的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#中csv文件與DataTable互相導(dǎo)入處理實(shí)例解析
這篇文章主要介紹了C#中csv文件與DataTable互相導(dǎo)入處理實(shí)例解析,非常實(shí)用的功能,需要的朋友可以參考下2014-08-08C#結(jié)合JS修改解決KindEditor彈出層問(wèn)題
KindEditor 是一款出色的富文本HTML在線編輯器,這里我們講述在使用中遇到的一個(gè)問(wèn)題,在部署到某些 WEB 應(yīng)用項(xiàng)目中,點(diǎn)擊類(lèi)似彈出層功能時(shí),只顯示了遮罩層,而內(nèi)容層則定位無(wú)法正確顯示,所以本文給大家介紹了C#結(jié)合JS 修改解決 KindEditor 彈出層問(wèn)題2024-06-06C# byte數(shù)組與Image相互轉(zhuǎn)換的方法
這篇文章介紹了C# byte數(shù)組與Image相互轉(zhuǎn)換的方法,有需要的朋友可以參考一下2013-10-10c#不使用系統(tǒng)api實(shí)現(xiàn)可以指定區(qū)域屏幕截屏功能
這篇文章主要介紹了不使用系統(tǒng)API通過(guò)純c#實(shí)現(xiàn)屏幕指定區(qū)域截屏功能,截屏后還可以保存圖象文件,大家參考使用吧2014-01-01淺談Visual Studio 2019 Vue項(xiàng)目的目錄結(jié)構(gòu)
這篇文章主要介紹了Visual Studio 2019 Vue項(xiàng)目 目錄結(jié)構(gòu),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03