轉:C#製作ORM映射學習筆記三 ORM映射實現

現在開始實現ORM的主體模塊,首先需要在項目中新建一個類,命名爲DbAccess,然後在項目的引用中添加兩個dll,分別是MySql.Data.dll和System.Data.SQLite.dll,這兩個dll都可以在對應的數據庫官網上下載到,爲了方便我這裏也提供一個下載地址。添加好dll後需要在DbAccess中添加幾個名空間,具體代碼如下:

using System;  
using System.Collections;  
using System.Collections.Generic;  
using System.Linq;  
using System.Data;  
using System.Data.Common;  
using System.Data.SQLite;  
using System.Reflection;  
using MySql.Data;  
using MySql.Data.MySqlClient;

 下面開始實現ORM,首先需要實現對數據庫的訪問,具體代碼如下:

private DbConnection dbConnection;  
  
private DbCommand dbCommand;  
  
private DbDataReader reader;  
  
//打開數據庫連接  
public void OpenDB()  
{  
    try  
    {  
        switch (DbConfig.Type)  
        {  
            case DbType.Sqlite: dbConnection = new SQLiteConnection("data source = " + DbConfig.Host); break;  
            case DbType.Mysql: dbConnection = new MySqlConnection(DbConfig.Host); break;  
            default: break;  
        }  
        dbConnection.Open();  
    }  
    catch (Exception e)  
    {  
        throw e;  
    }  
}  
  
//關閉數據庫連接  
public void CloseSqlConnection()  
{  
    if (dbCommand != null)  
    {  
        dbCommand.Dispose();  
    }  
    dbCommand = null;  
    if (reader != null)  
    {  
        reader.Dispose();  
    }  
    reader = null;  
    if (dbConnection != null && dbConnection.State == ConnectionState.Open)  
    {  
        dbConnection.Close();  
        dbConnection.Dispose();  
    }  
    dbConnection = null;  
}  
  
//執行Sql命令  
public int ExecuteQuery(string sql)  
{  
    OpenDB();  
    dbCommand = dbConnection.CreateCommand();  
    dbCommand.CommandText = sql;  
    reader = dbCommand.ExecuteReader();  
    return reader.RecordsAffected;  
}

實現了對數據庫的連接訪問後就可以開始具體實現ORM了,首先實現兩個查詢方法:FirstOrDefault和Fetch,分別實現查詢第一個滿足條件的記錄和查詢所有滿足條件的記錄,代碼如下: 

//查詢符合條件的第一個記錄  
public T FirstOrDefault<T>(Sql sql)  
{  
    try  
    {  
        ExecuteQuery(sql.GetSql());  
        T result = default(T);  
        if (reader.Read())  
        {  
            Type type = typeof(T);  
            if (type.IsPrimitive || type == typeof(string) || type == typeof(DateTime) || type.IsEnum)  
            {  
                if (type.IsEnum)  
                {  
                    result = (T)Enum.ToObject(type, reader.GetValue(0));  
                }  
                else  
                {  
                    result = (T)Convert.ChangeType(reader.GetValue(0), type);  
                }  
            }  
            else  
            {  
                result = Activator.CreateInstance<T>();  
                PropertyInfo[] properties = type.GetProperties();  
                foreach (PropertyInfo property in properties)  
                {  
                    string columName = AttributeProcess.GetColumnName(property);  
                    if (property.PropertyType.IsEnum)  
                    {  
                        property.SetValue(result, Enum.ToObject(property.PropertyType, reader.GetValue(reader.GetOrdinal(columName))), null);  
                    }  
                    else  
                    {  
                        property.SetValue(result, Convert.ChangeType(reader.GetValue(reader.GetOrdinal(columName)), property.PropertyType), null);  
                    }  
                }  
            }  
        }  
        return result;  
    }  
    catch (Exception e)  
    {  
        throw e;  
    }  
    finally  
    {  
        CloseSqlConnection();  
    }  
}  
  
//查詢所有符合條件的記錄  
public List<T> Fetch<T>(Sql sql)  
{  
    try  
    {  
        ExecuteQuery(sql.GetSql());  
        List<T> list = new List<T>();  
        Type type = typeof(T);  
        if (type.IsPrimitive || type == typeof(string) || type == typeof(DateTime) || type.IsEnum)  
        {  
            while (reader.Read())  
            {  
                if (type.IsEnum)  
                {  
                    list.Add((T)Enum.ToObject(type, reader.GetValue(0)));  
                }  
                else  
                {  
                    list.Add((T)Convert.ChangeType(reader.GetValue(0), type));  
                }  
            }  
        }  
        else  
        {  
            while (reader.Read())  
            {  
                T result = Activator.CreateInstance<T>();  
                PropertyInfo[] properties = type.GetProperties();  
                foreach (PropertyInfo property in properties)  
                {  
                    string columName = AttributeProcess.GetColumnName(property);  
                    if (property.PropertyType.IsEnum)  
                    {  
                        property.SetValue(result, Enum.ToObject(property.PropertyType, reader.GetValue(reader.GetOrdinal(columName))), null);  
                    }  
                    else  
                    {  
                        property.SetValue(result, Convert.ChangeType(reader.GetValue(reader.GetOrdinal(columName)), property.PropertyType), null);  
                    }  
                }  
                list.Add(result);  
            }  
        }  
        return list;  
    }  
    catch (Exception e)  
    {  
        throw e;  
    }  
    finally  
    {  
        CloseSqlConnection();  
    }  
}

 這裏有兩點需要注意,第一、一定要再finally中執行CloseSqlConnection,確保每次查詢結束後都會關閉連接,哪怕是查詢時出現異常。第二、對於只查詢一列的情況要特殊處理。
下面來實現增刪改三個方法,代碼如下:

/// <summary>  
/// 更新指定的列  
/// </summary>  
/// <typeparam name="T"></typeparam>  
/// <param name="data"></param>  
/// <param name="columns"></param>  
/// <returns></returns>  
public bool Update<T>(T data, IEnumerable<string> columns)  
{  
    try  
    {  
        if (columns == null || columns.Count() == 0)  
        {  
            Update<T>(data);  
        }  
        Type type = data.GetType();  
        string table = AttributeProcess.GetTableName(type);  
        string sql = "Update " + table + " Set ";  
        string where = " Where ";  
        List<string> sets = new List<string>();  
        PropertyInfo[] properties = type.GetProperties();  
        foreach (PropertyInfo property in properties)  
        {  
            string column = AttributeProcess.GetColumnName(property);  
            if (!AttributeProcess.IsPrimary(type, property))  
            {  
                if (columns.Any(a => a == column))  
                {  
                    if (property.PropertyType == typeof(bool))  
                    {  
                        bool value = bool.Parse(property.GetValue(data, null).ToString());  
                        sets.Add(column + "=" + (value ? "1" : "0"));  
                    }  
                    else if (property.PropertyType.IsPrimitive)  
                    {  
                        sets.Add(column + "=" + property.GetValue(data, null));  
                    }  
                    else if (property.PropertyType.IsEnum)  
                    {  
                        int intValue = (int)property.GetValue(data, null);  
                        sets.Add(column + "=" + intValue);  
                    }  
                    else  
                    {  
                        if (Sql.InjectionDefend(property.GetValue(data, null).ToString()))  
                        {  
                            sets.Add(column + "=\'" + property.GetValue(data, null) + "\'");  
                        }  
                    }  
                }  
            }  
            else  
            {  
                if (property.PropertyType.IsPrimitive)  
                {  
                    where += column + "=" + property.GetValue(data, null);  
                }  
                else  
                {  
                    where += column + "=\'" + property.GetValue(data, null) + "\'";  
                }  
            }  
        }  
        sql += (string.Join(",", sets) + where);  
        ExecuteQuery(sql);  
        return true;  
    }  
    catch (Exception e)  
    {  
        throw e;  
    }  
    finally  
    {  
        CloseSqlConnection();  
    }  
}  
  
//更新指定的記錄  
public bool Update<T>(T data)  
{  
    try  
    {  
        Type type = data.GetType();  
        string table = AttributeProcess.GetTableName(type);  
        string sql = "Update " + table + " Set ";  
        List<string> sets = new List<string>();  
        string where = " Where ";  
        PropertyInfo[] properties = type.GetProperties();  
        foreach (PropertyInfo property in properties)  
        {  
            string column = AttributeProcess.GetColumnName(property);  
            if (!AttributeProcess.IsPrimary(type, property))  
            {  
                if (property.PropertyType == typeof(bool))  
                {  
                    bool value = bool.Parse(property.GetValue(data, null).ToString());  
                    sets.Add(column + "=" + (value ? "1" : "0"));  
                }  
                else if (property.PropertyType.IsPrimitive)  
                {  
                    sets.Add(column + "=" + property.GetValue(data, null));  
                }  
                else if (property.PropertyType.IsEnum)  
                {  
                    int intValue = (int)property.GetValue(data, null);  
                    sets.Add(column + "=" + intValue);  
                }  
                else  
                {  
                    if (Sql.InjectionDefend(property.GetValue(data, null).ToString()))  
                    {  
                        sets.Add(column + "=\'" + property.GetValue(data, null) + "\'");  
                    }  
                }  
            }  
            else  
            {  
                if (property.PropertyType.IsPrimitive)  
                {  
                    where += column + "=" + property.GetValue(data, null);  
                }  
                else  
                {  
                    where += column + "=\'" + property.GetValue(data, null) + "\'";  
                }  
            }  
        }  
        sql += (string.Join(",", sets) + where);  
        ExecuteQuery(sql);  
        return true;  
    }  
    catch (Exception e)  
    {  
        throw e;  
    }  
    finally  
    {  
        CloseSqlConnection();  
    }  
}  
  
//插入新數據  
public bool Insert<T>(T data)  
{  
    try  
    {  
        Type type = data.GetType();  
        string table = AttributeProcess.GetTableName(type);  
        List<string> columns = new List<string>();  
        List<string> values = new List<string>();  
        PropertyInfo[] properties = type.GetProperties();  
        foreach (PropertyInfo property in properties)  
        {  
            if (!(AttributeProcess.IsPrimary(type, property) && AttributeProcess.IsIncrement(type)))  
            {  
                if (property.GetValue(data, null) != null)  
                {  
                    columns.Add(AttributeProcess.GetColumnName(property));  
                    if (property.PropertyType == typeof(bool))  
                    {  
                        bool value = bool.Parse(property.GetValue(data, null).ToString());  
                        values.Add((value ? "1" : "0"));  
                    }  
                    else if (property.PropertyType.IsPrimitive)  
                    {  
                        values.Add(property.GetValue(data, null).ToString());  
                    }  
                    else if (property.PropertyType.IsEnum)  
                    {  
                        int intValue = (int)property.GetValue(data, null);  
                        values.Add(intValue.ToString());  
                    }  
                    else  
                    {  
                        if (Sql.InjectionDefend(property.GetValue(data, null).ToString()))  
                        {  
                            values.Add("\'" + property.GetValue(data, null) + "\'");  
                        }  
                    }  
                }  
            }  
        }  
        string sql = "INSERT INTO " + table + "(" + string.Join(",", columns) + ")" + "VALUES" + "(" + string.Join(",", values) + ")";  
        ExecuteQuery(sql);  
        return true;  
    }  
    catch (Exception e)  
    {  
        throw e;  
    }  
    finally  
    {  
        CloseSqlConnection();  
    }  
}  
  
//刪除數據  
public bool Delete<T>(object id)  
{  
    try  
    {  
        Type type = typeof(T);  
        string table = AttributeProcess.GetTableName(type);  
        string sql = "DELETE FROM " + table + " WHERE ";  
        PropertyInfo[] properties = type.GetProperties();  
        foreach (PropertyInfo property in properties)  
        {  
            if (AttributeProcess.IsPrimary(type, property))  
            {  
                sql += (AttributeProcess.GetColumnName(property) + "=");  
                if (property.PropertyType.IsPrimitive)  
                {  
                    sql += (id.ToString() + ";");  
                }  
                else  
                {  
                    sql += ("\'" + id.ToString() + "\';");  
                }  
            }  
        }  
        ExecuteQuery(sql);  
        return true;  
    }  
    catch (Exception e)  
    {  
        throw e;  
    }  
    finally  
    {  
        CloseSqlConnection();  
    }  
}

上面我實現了兩個update,因爲如果只實現一個對一行數據的所有列的update的話,那麼在實現一些如更是狀態等只更新某幾列數據的功能時數據更新會變慢。此外還有兩點需要注意,第一、每個方法最後都要關閉數據庫訪問連接;第二update和insert中設置數據值的地方如果數據爲string類型需要進行防sql注入的操作。 

下面是完整代碼:

using System;  
using System.Collections;  
using System.Collections.Generic;  
using System.Linq;  
using System.Data;  
using System.Data.Common;  
using System.Data.SQLite;  
using System.Reflection;  
using MySql.Data;  
using MySql.Data.MySqlClient;  
  
namespace ORM  
{  
    public class DbAccess  
    {  
        private DbConnection dbConnection;  
  
        private DbCommand dbCommand;  
  
        private DbDataReader reader;  
  
        //打開數據庫連接  
        public void OpenDB()  
        {  
            try  
            {  
                switch (DbConfig.Type)  
                {  
                    case DbType.Sqlite: dbConnection = new SQLiteConnection("data source = " + DbConfig.Host); break;  
                    case DbType.Mysql: dbConnection = new MySqlConnection(DbConfig.Host); break;  
                    default: break;  
                }  
                dbConnection.Open();  
            }  
            catch (Exception e)  
            {  
                throw e;  
            }  
        }  
  
        //關閉數據庫連接  
        public void CloseSqlConnection()  
        {  
            if (dbCommand != null)  
            {  
                dbCommand.Dispose();  
            }  
            dbCommand = null;  
            if (reader != null)  
            {  
                reader.Dispose();  
            }  
            reader = null;  
            if (dbConnection != null && dbConnection.State == ConnectionState.Open)  
            {  
                dbConnection.Close();  
                dbConnection.Dispose();  
            }  
            dbConnection = null;  
        }  
  
        //執行Sql命令  
        public int ExecuteQuery(string sql)  
        {  
            OpenDB();  
            dbCommand = dbConnection.CreateCommand();  
            dbCommand.CommandText = sql;  
            reader = dbCommand.ExecuteReader();  
            return reader.RecordsAffected;  
        }  
  
        //查詢符合條件的第一個記錄  
        public T FirstOrDefault<T>(Sql sql)  
        {  
            try  
            {  
                ExecuteQuery(sql.GetSql());  
                T result = default(T);  
                if (reader.Read())  
                {  
                    Type type = typeof(T);  
                    if (type.IsPrimitive || type == typeof(string) || type == typeof(DateTime) || type.IsEnum)  
                    {  
                        if (type.IsEnum)  
                        {  
                            result = (T)Enum.ToObject(type, reader.GetValue(0));  
                        }  
                        else  
                        {  
                            result = (T)Convert.ChangeType(reader.GetValue(0), type);  
                        }  
                    }  
                    else  
                    {  
                        result = Activator.CreateInstance<T>();  
                        PropertyInfo[] properties = type.GetProperties();  
                        foreach (PropertyInfo property in properties)  
                        {  
                            string columName = AttributeProcess.GetColumnName(property);  
                            if (property.PropertyType.IsEnum)  
                            {  
                                property.SetValue(result, Enum.ToObject(property.PropertyType, reader.GetValue(reader.GetOrdinal(columName))), null);  
                            }  
                            else  
                            {  
                                property.SetValue(result, Convert.ChangeType(reader.GetValue(reader.GetOrdinal(columName)), property.PropertyType), null);  
                            }  
                        }  
                    }  
                }  
                return result;  
            }  
            catch (Exception e)  
            {  
                throw e;  
            }  
            finally  
            {  
                CloseSqlConnection();  
            }  
        }  
  
        //查詢所有符合條件的記錄  
        public List<T> Fetch<T>(Sql sql)  
        {  
            try  
            {  
                ExecuteQuery(sql.GetSql());  
                List<T> list = new List<T>();  
                Type type = typeof(T);  
                if (type.IsPrimitive || type == typeof(string) || type == typeof(DateTime) || type.IsEnum)  
                {  
                    while (reader.Read())  
                    {  
                        if (type.IsEnum)  
                        {  
                            list.Add((T)Enum.ToObject(type, reader.GetValue(0)));  
                        }  
                        else  
                        {  
                            list.Add((T)Convert.ChangeType(reader.GetValue(0), type));  
                        }  
                    }  
                }  
                else  
                {  
                    while (reader.Read())  
                    {  
                        T result = Activator.CreateInstance<T>();  
                        PropertyInfo[] properties = type.GetProperties();  
                        foreach (PropertyInfo property in properties)  
                        {  
                            string columName = AttributeProcess.GetColumnName(property);  
                            if (property.PropertyType.IsEnum)  
                            {  
                                property.SetValue(result, Enum.ToObject(property.PropertyType, reader.GetValue(reader.GetOrdinal(columName))), null);  
                            }  
                            else  
                            {  
                                property.SetValue(result, Convert.ChangeType(reader.GetValue(reader.GetOrdinal(columName)), property.PropertyType), null);  
                            }  
                        }  
                        list.Add(result);  
                    }  
                }  
                return list;  
            }  
            catch (Exception e)  
            {  
                throw e;  
            }  
            finally  
            {  
                CloseSqlConnection();  
            }  
        }  
  
        /// <summary>  
        /// 更新指定的列  
        /// </summary>  
        /// <typeparam name="T"></typeparam>  
        /// <param name="data"></param>  
        /// <param name="columns"></param>  
        /// <returns></returns>  
        public bool Update<T>(T data, IEnumerable<string> columns)  
        {  
            try  
            {  
                if (columns == null || columns.Count() == 0)  
                {  
                    Update<T>(data);  
                }  
                Type type = data.GetType();  
                string table = AttributeProcess.GetTableName(type);  
                string sql = "Update " + table + " Set ";  
                string where = " Where ";  
                List<string> sets = new List<string>();  
                PropertyInfo[] properties = type.GetProperties();  
                foreach (PropertyInfo property in properties)  
                {  
                    string column = AttributeProcess.GetColumnName(property);  
                    if (!AttributeProcess.IsPrimary(type, property))  
                    {  
                        if (columns.Any(a => a == column))  
                        {  
                            if (property.PropertyType == typeof(bool))  
                            {  
                                bool value = bool.Parse(property.GetValue(data, null).ToString());  
                                sets.Add(column + "=" + (value ? "1" : "0"));  
                            }  
                            else if (property.PropertyType.IsPrimitive)  
                            {  
                                sets.Add(column + "=" + property.GetValue(data, null));  
                            }  
                            else if (property.PropertyType.IsEnum)  
                            {  
                                int intValue = (int)property.GetValue(data, null);  
                                sets.Add(column + "=" + intValue);  
                            }  
                            else  
                            {  
                                if (Sql.InjectionDefend(property.GetValue(data, null).ToString()))  
                                {  
                                    sets.Add(column + "=\'" + property.GetValue(data, null) + "\'");  
                                }  
                            }  
                        }  
                    }  
                    else  
                    {  
                        if (property.PropertyType.IsPrimitive)  
                        {  
                            where += column + "=" + property.GetValue(data, null);  
                        }  
                        else  
                        {  
                            where += column + "=\'" + property.GetValue(data, null) + "\'";  
                        }  
                    }  
                }  
                sql += (string.Join(",", sets) + where);  
                ExecuteQuery(sql);  
                return true;  
            }  
            catch (Exception e)  
            {  
                throw e;  
            }  
            finally  
            {  
                CloseSqlConnection();  
            }  
        }  
  
        //更新指定的記錄  
        public bool Update<T>(T data)  
        {  
            try  
            {  
                Type type = data.GetType();  
                string table = AttributeProcess.GetTableName(type);  
                string sql = "Update " + table + " Set ";  
                List<string> sets = new List<string>();  
                string where = " Where ";  
                PropertyInfo[] properties = type.GetProperties();  
                foreach (PropertyInfo property in properties)  
                {  
                    string column = AttributeProcess.GetColumnName(property);  
                    if (!AttributeProcess.IsPrimary(type, property))  
                    {  
                        if (property.PropertyType == typeof(bool))  
                        {  
                            bool value = bool.Parse(property.GetValue(data, null).ToString());  
                            sets.Add(column + "=" + (value ? "1" : "0"));  
                        }  
                        else if (property.PropertyType.IsPrimitive)  
                        {  
                            sets.Add(column + "=" + property.GetValue(data, null));  
                        }  
                        else if (property.PropertyType.IsEnum)  
                        {  
                            int intValue = (int)property.GetValue(data, null);  
                            sets.Add(column + "=" + intValue);  
                        }  
                        else  
                        {  
                            if (Sql.InjectionDefend(property.GetValue(data, null).ToString()))  
                            {  
                                sets.Add(column + "=\'" + property.GetValue(data, null) + "\'");  
                            }  
                        }  
                    }  
                    else  
                    {  
                        if (property.PropertyType.IsPrimitive)  
                        {  
                            where += column + "=" + property.GetValue(data, null);  
                        }  
                        else  
                        {  
                            where += column + "=\'" + property.GetValue(data, null) + "\'";  
                        }  
                    }  
                }  
                sql += (string.Join(",", sets) + where);  
                ExecuteQuery(sql);  
                return true;  
            }  
            catch (Exception e)  
            {  
                throw e;  
            }  
            finally  
            {  
                CloseSqlConnection();  
            }  
        }  
  
        //插入新數據  
        public bool Insert<T>(T data)  
        {  
            try  
            {  
                Type type = data.GetType();  
                string table = AttributeProcess.GetTableName(type);  
                List<string> columns = new List<string>();  
                List<string> values = new List<string>();  
                PropertyInfo[] properties = type.GetProperties();  
                foreach (PropertyInfo property in properties)  
                {  
                    if (!(AttributeProcess.IsPrimary(type, property) && AttributeProcess.IsIncrement(type)))  
                    {  
                        if (property.GetValue(data, null) != null)  
                        {  
                            columns.Add(AttributeProcess.GetColumnName(property));  
                            if (property.PropertyType == typeof(bool))  
                            {  
                                bool value = bool.Parse(property.GetValue(data, null).ToString());  
                                values.Add((value ? "1" : "0"));  
                            }  
                            else if (property.PropertyType.IsPrimitive)  
                            {  
                                values.Add(property.GetValue(data, null).ToString());  
                            }  
                            else if (property.PropertyType.IsEnum)  
                            {  
                                int intValue = (int)property.GetValue(data, null);  
                                values.Add(intValue.ToString());  
                            }  
                            else  
                            {  
                                if (Sql.InjectionDefend(property.GetValue(data, null).ToString()))  
                                {  
                                    values.Add("\'" + property.GetValue(data, null) + "\'");  
                                }  
                            }  
                        }  
                    }  
                }  
                string sql = "INSERT INTO " + table + "(" + string.Join(",", columns) + ")" + "VALUES" + "(" + string.Join(",", values) + ")";  
                ExecuteQuery(sql);  
                return true;  
            }  
            catch (Exception e)  
            {  
                throw e;  
            }  
            finally  
            {  
                CloseSqlConnection();  
            }  
        }  
  
        //刪除數據  
        public bool Delete<T>(object id)  
        {  
            try  
            {  
                Type type = typeof(T);  
                string table = AttributeProcess.GetTableName(type);  
                string sql = "DELETE FROM " + table + " WHERE ";  
                PropertyInfo[] properties = type.GetProperties();  
                foreach (PropertyInfo property in properties)  
                {  
                    if (AttributeProcess.IsPrimary(type, property))  
                    {  
                        sql += (AttributeProcess.GetColumnName(property) + "=");  
                        if (property.PropertyType.IsPrimitive)  
                        {  
                            sql += (id.ToString() + ";");  
                        }  
                        else  
                        {  
                            sql += ("\'" + id.ToString() + "\';");  
                        }  
                    }  
                }  
                ExecuteQuery(sql);  
                return true;  
            }  
            catch (Exception e)  
            {  
                throw e;  
            }  
            finally  
            {  
                CloseSqlConnection();  
            }  
        }  
    }  
}

 在之前建立的userinfo表中插入一條數據用於測試,如下圖:


然後在main函數中添加如下代碼進行測試:

 

static void Main(string[] args)  
{  
    DbAccess dao = new DbAccess();  
    Sql sql = new Sql();  
    sql.Select("*").From("userinfo");  
    sql.Where("Id=@0", 1);  
    User user = dao.FirstOrDefault<User>(sql);  
    Console.WriteLine(user.UserName);  
    user.UserName = "tczhoulan";  
    Console.WriteLine(dao.Update<User>(user, new string[] { "UserName" }));  
}

 

執行結果如下圖:


再看數據庫中的數據,UserName中的值已經被修改了,如下圖所示:

到這裏一個簡單的ORM映射框架就基本完成了,當然這只是一個最簡單的ORM框架,其中還有許多不完善的地方,如有需要可以自己在上面進行擴充,我也會慢慢的進行完善。

 轉自:https://www.cnblogs.com/vaevvaev/p/7054951.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章