動態類型序列化

来源:http://www.cnblogs.com/Skyven/archive/2017/11/06/7795535.html
-Advertisement-
Play Games

上一篇文章直接就被移除首頁了,這次來點大家都能懂的乾貨. 需求 之前做一個winform的工具時候有以下幾個需求1. 主窗體(或者叫平臺)可以安裝若幹類型的插件。2. 插件關閉時候需要保存狀態。3. 插件載入的時候可以載入上次關閉的配置。4. 插件中的配置可以切換。5. 主窗體本身保存當前插件,並且 ...


上一篇文章直接就被移除首頁了,這次來點大家都能懂的乾貨.

需求

之前做一個winform的工具時候有以下幾個需求
1. 主窗體(或者叫平臺)可以安裝若幹類型的插件。
2. 插件關閉時候需要保存狀態。
3. 插件載入的時候可以載入上次關閉的配置。
4. 插件中的配置可以切換。
5. 主窗體本身保存當前插件,並且可以通過不同的配置文件切換插件

使用上最方便的做法是將配置給平臺來管理。但是平臺本身並不知道插件要保存怎樣的配置。針對以上問題在配置這個上做瞭如下設計

設計

1. 動態類型序列化以滿足插件的任何配置需要
2. 動態類型基本的就是dynamic,那麼我們需用字典作為實現
3. 支持具體的類進行序列化,那麼此時需要用xml保存類的元數據信息
4. 支持介面的序列化,此時也是保存實際類型的元數據信息
5. 支持List序列化
6. 支持Arry序列化
7. 支持Dictionary序列化

介面定義


其中PathOrSourceString 屬性這樣既可以支持文件,也可以直接支持字元串,擴展更加方便.

public interface IConfig
    {
        string PathOrSourceString { get; set; }

        dynamic Data { get; set; }
    }

 

動態類型實現


這裡是基於字典,網上有很多類似的代碼。

這裡字典的Value設計成dynamic是為了嵌套。

    [Serializable]
    public class DynamicDictionary : DynamicObject
    {
        private Dictionary<string, dynamic> _dictionary = new Dictionary<string, dynamic>();

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            string name = binder.Name;
            if (!_dictionary.ContainsKey(name))
            {
                _dictionary.Add(name, new DynamicDictionary());
            }
            return _dictionary.TryGetValue(name, out result);
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            var key = binder.Name;
            if (_dictionary.ContainsKey(key))
                _dictionary[key] = value;
            else
            {
                _dictionary.Add(key, value);
            }


            return true;
        }

        public Dictionary<string, dynamic> Dictionary
        {
            get { return _dictionary; }
        }

        public void AddMember(string name, dynamic value)
        {
            _dictionary.Add(name, value);
        }
    }

  

配置的載入和保存邏輯(核心)

 

    public static class ConfigManager
    {
        public static IConfig LoadFromFile(this IConfig config)
        {
            if (config == null || string.IsNullOrEmpty(config.PathOrSourceString))
                throw new ArgumentNullException("config");
            if (!File.Exists(config.PathOrSourceString))
            {
                return config;
            }
            var doc = new XmlDocument();
            doc.Load(config.PathOrSourceString);
            var element = doc["Data"];
            config.Data = GetValue(element);
            return config;
        }

        public static IConfig SaveToFile(this IConfig config)
        {
            if (config == null || string.IsNullOrEmpty(config.PathOrSourceString) || config.Data == null)
                throw new ArgumentNullException("config");
            var dir = Path.GetDirectoryName(config.PathOrSourceString);
            if (!Directory.Exists(dir))
                Directory.CreateDirectory(dir);
            var doc = new XmlDocument();
            doc.AppendChild(GetXml("Data", config.Data, doc));
            doc.Save(config.PathOrSourceString);
            return config;
        }

        public static IConfig LoadFromString(this IConfig config)
        {
            if (config == null || string.IsNullOrEmpty(config.PathOrSourceString))
                throw new ArgumentNullException("config");
            var doc = new XmlDocument();
            doc.LoadXml(config.PathOrSourceString);
            var element = doc["Data"];
            config.Data = GetValue(element);
            return config;
        }

        public static IConfig SaveToString(this IConfig config)
        {
            if (config == null || config.Data == null)
                throw new ArgumentNullException("config");
            var doc = new XmlDocument();
            doc.AppendChild(GetXml("Data", config.Data, doc));
            config.PathOrSourceString = doc.OuterXml;
            return config;
        }

        #region 解析XmlElement

        public static dynamic GetValue(XmlElement element)
        {
            if (element == null)
                return null;

            Classify clasify;
            Enum.TryParse(element.GetAttribute("Classify"), out clasify);
            switch (clasify)
            {
                case Classify.Sample:
                    return GetSampleValue(element.GetAttribute("Assembly"), element.GetAttribute("Type"), element.InnerText);
                case Classify.Array:
                    return GetArrayValue(element.GetAttribute("ElementAssembly"), element.GetAttribute("ElementType"), element.GetChidlren());
                case Classify.List:
                    return GetListValue(element.GetAttribute("GenericAssembly"), element.GetAttribute("GenericType"), element.GetChidlren());
                case Classify.Dictionary:
                    return GetDictionaryValue(element.GetAttribute("KeyGenericAssembly"),
                        element.GetAttribute("KeyGenericType"),
                        element.GetAttribute("ValueGenericAssembly"),
                        element.GetAttribute("ValueGenericType"),
                        element.GetChidlren());
                case Classify.Dynamic:
                    return GetDynamicValue(element.GetChidlren());
                case Classify.Custom:
                    return GetCustomValue(element.GetAttribute("Assembly"), element.GetAttribute("Type"), element.GetChidlren());
            }

            return null;
        }
        public static object GetSampleValue(string assembly, string typeFullName, string value)
        {
            var type = Assembly.Load(assembly).GetType(typeFullName);
            if (type == null)
                return null;
            return CoralConvert.Convert(value, type);
        }
        public static object GetListValue(string genericAssembly, string genericTypeName, List<XmlElement> elements)
        {
            var genericType = Assembly.Load(genericAssembly).GetType(genericTypeName);
            var type = typeof(List<>).MakeGenericType(genericType);
            dynamic list = Activator.CreateInstance(type, true);

            foreach (var element in elements)
            {
                list.Add(GetValue(element));
            }
            return list;
        }
        public static object GetArrayValue(string elementAssembly, string elementTypeName, List<XmlElement> elements)
        {
            var elementType = Assembly.Load(elementAssembly).GetType(elementTypeName);
            dynamic list = Array.CreateInstance(elementType, elements.Count);
            for (int i = 0; i < elements.Count; i++)
            {
                list[i] = GetValue(elements[i]);
            }
            return list;
        }
        public static object GetDictionaryValue(string keyAssembly, string keyTypeName, string valueAssembly, string valueTypeName, List<XmlElement> elements)
        {
            var keyType = Assembly.Load(keyAssembly).GetType(keyTypeName);
            var valueType = Assembly.Load(valueAssembly).GetType(valueTypeName);
            var type = typeof(Dictionary<,>).MakeGenericType(keyType, valueType);
            dynamic dict = Activator.CreateInstance(type, true);
            foreach (var element in elements)
            {
                dict.Add(GetValue(element["Key"]), GetValue(element["Value"]));
            }
            return dict;
        }
        public static object GetDynamicValue(List<XmlElement> elements)
        {
            var dict = new DynamicDictionary();
            foreach (var element in elements)
            {
                dict.Dictionary.Add(GetValue(element["Key"]), GetValue(element["Value"]));
            }
            return dict;
        }
        public static object GetCustomValue(string assemblyFullName, string typeFullName, List<XmlElement> elements)
        {
            var type = Assembly.Load(assemblyFullName).GetType(typeFullName);
            if (type == null)
                return null;
            dynamic obj = Activator.CreateInstance(type, true);

            foreach (var element in elements)
            {
                var property = type.GetProperty(element.Name);
                object value;
                if (!CoralConvert.Convert(GetValue(element), property.PropertyType, out value))
                    continue;
                property.SetValue(obj, value);
            }

            return obj;
        }
        #endregion

        #region 創建XmlElement

        /// <summary>
        /// 創建xml元素
        /// </summary>
        /// <param name="name"></param>
        /// <param name="data"></param>
        /// <param name="doc"></param>
        /// <returns></returns>
        public static XmlElement GetXml(string name, object data, XmlDocument doc)
        {
            if (data == null)
                return null;
            if (data.GetType().IsValueType || data is string)
            {
                return GetValueTypeXml(name, data, doc);
            }
            var list = data as IList;
            if (list != null)
            {
                return GetIListXml(name, list, doc);
            }
            var dict = data as IDictionary;
            if (dict != null)
            {
                return GetIDictionaryXml(name, dict, doc);
            }
            var dynamic = data as DynamicDictionary;
            if (dynamic != null)
            {
                return GetDynamicXml(name, dynamic, doc);
            }
            return GetCustomXml(name, data, doc);
        }

        /// <summary>
        /// 創建簡單類型的xml元素
        /// </summary>
        /// <param name="name"></param>
        /// <param name="data"></param>
        /// <param name="doc"></param>
        /// <returns></returns>
        private static XmlElement GetValueTypeXml(string name, object data, XmlDocument doc)
        {
            if (data == null)
                return null;
            var element = doc.CreateElement(name);
            element.SetAttribute("Type", data.GetType().FullName);
            element.SetAttribute("Assembly", MetaDataManager.Assembly.GetAssemblySortName(data.GetType().Assembly));
            element.SetAttribute("Classify", Classify.Sample.ToString());
            element.InnerText = data.ToString();
            return element;
        }

        /// <summary>
        /// 獲取列表類型的xml
        /// </summary>
        /// <param name="name"></param>
        /// <param name="datas"></param>
        /// <param name="doc"></param>
        /// <returns></returns>
        private static XmlElement GetIListXml(string name, object datas, XmlDocument doc)
        {
            if (datas == null)
                return null;
            var element = doc.CreateElement(name);
            if (datas.GetType().IsArray)
            {
                element.SetAttribute("Type", typeof(Array).FullName);
                element.SetAttribute("Classify", Classify.Array.ToString());
                element.SetAttribute("ElementType", datas.GetType().GetElementType().FullName);
                element.SetAttribute("ElementAssembly", datas.GetType().GetElementType().Assembly.FullName);
            }
            else
            {
                element.SetAttribute("Type", typeof(IList).FullName);
                element.SetAttribute("Classify", Classify.List.ToString());
                element.SetAttribute("GenericType", datas.GetType().GenericTypeArguments[0].FullName);
                element.SetAttribute("GenericAssembly", datas.GetType().GenericTypeArguments[0].Assembly.FullName);
            }
            foreach (var data in (IList)datas)
            {
                element.AppendChild(GetXml("Element", data, doc));
            }
            return element;
        }

        /// <summary>
        /// 創建動態類型的xml
        /// </summary>
        /// <param name="name"></param>
        /// <param name="data"></param>
        /// <param name="doc"></param>
        /// <returns></returns>
        private static XmlElement GetDynamicXml(string name, dynamic data, XmlDocument doc)
        {
            if (data == null)
                return null;
            var element = doc.CreateElement(name);
            element.SetAttribute("Type", "dynamic");
            element.SetAttribute("Classify", Classify.Dynamic.ToString());
            foreach (DictionaryEntry item in (IDictionary)data.Dictionary)
            {
                var child = doc.CreateElement("Element");
                child.AppendChild(GetXml("Key", item.Key ?? string.Empty, doc));
                child.AppendChild(GetXml("Value", item.Value ?? string.Empty, doc));
                element.AppendChild(child);
            }
            return element;
        }

        /// <summary>
        /// 創建字典類型的xml
        /// </summary>
        /// <param name="name"></param>
        /// <param name="datas"></param>
        /// <param name="doc"></param>
        /// <returns></returns>
        private static XmlElement GetIDictionaryXml(string name, object datas, XmlDocument doc)
        {
            if (datas == null)
                return null;
            var element = doc.CreateElement(name);
            element.SetAttribute("Type", typeof(IDictionary).FullName);
            element.SetAttribute("Classify", Classify.Dictionary.ToString());
            element.SetAttribute("KeyGenericAssembly", datas.GetType().GetGenericArguments()[0].Assembly.FullName);
            element.SetAttribute("KeyGenericType", datas.GetType().GetGenericArguments()[0].FullName);
            element.SetAttribute("ValueGenericAssembly", datas.GetType().GetGenericArguments()[1].Assembly.FullName);
            element.SetAttribute("ValueGenericType", datas.GetType().GetGenericArguments()[1].FullName);
            foreach (DictionaryEntry data in (IDictionary)datas)
            {
                var child = doc.CreateElement("Element");
                child.AppendChild(GetXml("Key", data.Key ?? string.Empty, doc));
                child.AppendChild(GetXml("Value", data.Value ?? string.Empty, doc));
                element.AppendChild(child);
            }
            return element;
        }

        /// <summary>
        /// 創建自定義類
        /// </summary>
        /// <param name="name"></param>
        /// <param name="data"></param>
        /// <param name="doc"></param>
        /// <returns></returns>
        private static XmlElement GetCustomXml(string name, object data, XmlDocument doc)
        {
            if (data == null)
                return null;
            var element = doc.CreateElement(name);
            element.SetAttribute("Assembly",MetaDataManager.Assembly.GetAssemblySortName(data.GetType().Assembly));
            element.SetAttribute("Type", data.GetType().FullName);
            element.SetAttribute("Classify", Classify.Custom.ToString());
            data.GetType().GetProperties().ForEach(property =>
            {
                var item = GetXml(property.Name, property.GetValue(data), doc);
                if (item != null)
                    element.AppendChild(item);
            });
            return element;
        }

        #endregion

        public enum Classify
        {
            Sample,
            List,
            Array,
            Dictionary,
            Dynamic,
            Custom,
        }
        public static List<XmlElement> GetChidlren(this XmlElement element)
        {
            return element.Cast<XmlElement>().ToList();
        }
    }

  

  

核心思路就是遞歸,充分利用元數據

 

測試代碼

 public class XmlConfig : IConfig
    {
        public string PathOrSourceString { get; set; }
        public dynamic Data { get; set; }
    }

    public interface ITestModel
    {

        string Name { get; set; }

        string DataType { get; set; }

        string Data { get; set; }


    }

    public class TestConfig
    {
        public ITestModel Model { get; set; }

        public  List<ITestModel> List { get; set; }
        public Dictionary<string, ITestModel> Dict { get; set; }
    }



    public class TestModel: ITestModel
    {
        public string Name { get; set; }

        public string DataType { get; set; }

        public string Data { get; set; }

        public List<ITestModel> List { get; set; }
        public Dictionary<string, ITestModel> Dict { get; set; }
    }


    public class ConfigTest
    {
        public static void PerformanceTest()
        {
            var xmlconfig = new XmlConfig();

            xmlconfig.PathOrSourceString = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs", "test1.Config");

            #region list 類,字典測試
            
            var testModel=   new TestModel()
            {
                Name = "1",
                DataType = "1",
                Data = "1",
                List = new List<ITestModel>
                {
                    new TestModel
                    {
                        Name = "2",
                        DataType = "2",
                        Data = "2",
                    },
                    new TestModel
                    {
                        Name = "3",
                        DataType = "3",
                        Data = "3",
                    },
                },
                Dict = new Dictionary<string, ITestModel>
                {
                    {"4", new TestModel
                            {
                                Name = "4",
                                DataType = "4",
                                Data = "4",
                            }
                     },
                    {"5", new TestModel
                            {
                                Name = "5",
                                DataType = "5",
                                Data = "5",
                            }
                     },
                }
            };
            #endregion

            xmlconfig.Data = new TestConfig()
            {
                Model = testModel,
                Dict = new Dictionary<string, ITestModel>()
                {
                    {"1",testModel },
                    {"2",testModel }
                },
                List = new List<ITestModel> { testModel,testModel}
            };


            #region 動態類型,類,list,字典總和測試

            xmlconfig.Data = new DynamicDictionary();

            xmlconfig.Data.Name = "Test1";
            xmlconfig.Data.DataType = "Test1";

            xmlconfig.Data.List = new List<TestModel>
            {
                new TestModel
                {
                    Name = "2",
                    DataType = "2",
                    Data = "2",
                },
                new TestModel
                {
                    Name = "3",
                    DataType = "3",
                    Data = "3",
                },
            };
            xmlconfig.Data.Dict = new Dictionary<string, TestModel>
            {
                {
                    "4", new TestModel
                    {
                        Name = "4",
                        DataType = "4",
                        Data = "4",
                    }
                },
                {
                    "5", new TestModel
                    {
                        Name = "5",
                        DataType = "5",
                        Data = "5",
                    }
                },
            };
            xmlconfig.Data.Other.Name = "Test1";
            xmlconfig.Data.Other.DataType = "Test1";

            #endregion

            xmlconfig.SaveToFile();


            var data = new XmlConfig();

            data.PathOrSourceString = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs", "test1.Config");
            data.LoadFromFile();
        }

 配置文件為

<Data Type="dynamic" Classify="Dynamic">
  <Element>
    <Key Type="System.String" Assembly="mscorlib" Classify="Sample">Name</Key>
    <Value Type="System.String" Assembly="mscorlib" Classify="Sample">Test1</Value>
  </Element>
  <Element>
    <Key Type="System.String" Assembly="mscorlib" Classify="Sample">DataType</Key>
    <Value Type="System.String" Assembly="mscorlib" Classify="Sample">Test1</Value>
  </Element>
  <Element>
    <Key Type="System.String" Assembly="mscorlib" Classify="Sample">List</Key>
    <Value Type="System.Collections.IList" Classify="List" GenericType="RunnerTest.Common.TestModel" GenericAssembly="RunnerTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
      <Element Assembly="RunnerTest" Type="RunnerTest.Common.TestModel" Classify="Custom">
        <Name Type="System.String" Assembly="mscorlib" Classify="Sample">2</Name>
        <DataType Type="System.String" Assembly="mscorlib" Classify="Sample">2</DataType>
        <Data Type="System.String" Assembly="mscorlib" Classify="Sample">2</Data>
      </Element>
      <Element Assembly="RunnerTest" Type="RunnerTest.Common.TestModel" Classify="Custom">
        <Name Type="System.String" Assembly="mscorlib" Classify="Sample">3</Name>
        <DataType Type="System.String" Assembly="mscorlib" Classify="Sample">3</DataType>
        <Data Type="System.String" Assembly="mscorlib" Classify="Sample">3</Data>
      </Element>
    </Value>
  </Element>
  <Element>
    <Key Type="System.String" Assembly="mscorlib" Classify="Sample">Dict</Key>
    <Value Type="System.Collections.IDictionary" Classify="Dictionary" KeyGenericAssembly="mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" KeyGenericType="System.String" ValueGenericAssembly="RunnerTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" ValueGenericType="RunnerTest.Common.TestModel">
      <Element>
        <Key Type="System.String" Assembly="mscorlib" Classify="Sample">4</Key>
        <Value Assembly="RunnerTest" Type="RunnerTest.Common.TestModel" Classify="Custom">
          <Name Type="System.String" Assembly="mscorlib" Classify="Sample">4</Name>
          <DataType Type="System.String" Assembly="mscorlib" Classify="Sample">4</DataType>
          <Data Type="System.String" Assembly="mscorlib" Classify="Sample">4</Data>
        </Value>
      </Element>
      <Element>
        <Key Type="System.String" Assembly="mscorlib" Classify="Sample">5</Key>
        <Value Assembly="RunnerTest" Type="RunnerTest.Common.TestModel" Classify="Custom">
          <Name Type="System.String" Assembly="mscorlib" Classify="Sample">5</Name>
          <DataType Type="System.String" Assembly="mscorlib" Classify="Sample">5</DataType>
          <Data Type="System.String" Assembly="mscorlib" Classify="Sample">5</Data>
        </Value>
      </Element>
    </Value>
  </Element>
  <Element>
    <Key Type="System.String" Assembly="mscorlib" Classify="Sample">Other</Key>
    <Value Type="dynamic" Classify="Dynamic">
      <Element>
        <Key Type="System.String" Assembly="mscorlib" Classify="Sample">Name</Key>
        <Value Type="System.String" Assembly="mscorlib" Classify="Sample">Test1</Value>
      </Element>
      <Element>
        <Key Type="System.String" Assembly="mscorlib" Classify="Sample">DataType</Key>
        <Value Type="System.String" Assembly="mscorlib" Classify="Sample">Test1</Value>
      </Element>
    </Value>
  </Element>
</Data>

  

說明


最大的用處,你拿到一個對象未知的對象,並不需要知道他的實際類型,就可以進行持久化,並且讀取出來之後能夠還原到原始類型。


實現這部分我覺得在於以下幾個點

1. 對元數據的充分理解
2. 對xml結構的充分理解
3. 需要一點寫演算法的能力


我覺得代碼本身並不複雜,只要耐心單步調試都能看懂。

當然這個是有一定限制的:

1. 可讀性不強,所以在需要從文件進行修改配置比較麻煩

2.不可跨系統,文件中類型從程式集載入不到時就會出錯

3.性能不高.性能敏感的部分不太適合


所以這部分功能需要結合業務場景使用,在我這裡,包含作業調度系統,統計系統,介面測試工具中有使用.

這其實特別想WSDL的Soap協議,文件中既包含元數據的說明,又包含數據本身.真個元數據變成也是一個做設計時候一個重要思想。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近在看一些關於游戲引擎的東西,本來是有幾個游戲的小點子,其實實現起來還挺麻煩的,想找個游戲引擎看看能不能碼起來。輾轉之後發現了很多2D引擎,其中國產的要數cocos2dx用的好像是比較廣泛,但是好多人對此褒貶不一。於是下了準備試試到底怎麼樣,無奈搞了一早上,也有點小成果,但是想實現起來貌似還得花點 ...
  • 如何在ArcGIS中將一個欄位下有特征的需要分開的內容分別批量賦值給其他兩個欄位 觀察發現可以通過中間的逗號分為前後兩個部分,然後替換掉每個部分中多餘的內容,即可得到結果分開可以通過Python的截取字元串方法得到,註意python的格式代碼如下://根據逗號分開前後兩部分,分別賦值給兩列//ww列 ...
  • Tornado特點一句話簡介:Tornado是非阻塞式的Web伺服器,速度非常快,每秒可以處理數以千計的鏈接,因此Tornado是實時Web服務的一個理想框架。 一、安裝Tornado使用pip安裝即可:pip install tornado 二、運行Tornado的helloworld所需的基本組 ...
  • 前言 轉載請標明出處:http://www.cnblogs.com/smfx1314/p/7795837.html 本項目是我寫的一個練習,目的是回顧ssh框架的整合以及使用。項目介紹:此項目主要有前臺管理員通過登錄進入員工管理系統頁面,之後可以對員工列表進行常規的增刪改查。以及部門列表的增刪改查。 ...
  • 1 /*造人*/ 2 public class Tman { 3 public int id; 4 public String name; 5 public int age; 6 public String city; 7 public String introduce() { 8 return + ...
  • if語句應用之——求最大值 while迴圈 例題1:輸出1到100之間的偶數-(不過我覺得我的程式好蠢) 簡化的: 註意: while 後的條件判斷語句,一定要在程式中做flase打斷,不然會無休止的運行下去 ...
  • demo需求: 用java第三方調用郵箱(主要是qq郵箱)發送郵件給一方或多方 demo主要技術: 1.引入一個額外的jar包,javax.mail.jar,開啟java操作郵箱的功能 2.Properties類的使用 demo主要代碼展示: Java / show 方法簡介 第三方調用郵箱(這裡主 ...
  • name=[ 'coco', 'liux', 'zhangyq', 'liufupengdozhao']# 使用rang()方法for i in range(0,len(name)): print(i,name[i])# 上面的方法有些累贅,使用內置enumerrate函數會有更加直接for i i... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...