如何重构静态类中的代码复制?

8tntrjer  于 2021-06-25  发布在  Mysql
关注(0)|答案(0)|浏览(271)

下面是一段代码,它是一个静态类的一部分,它给我重构带来了麻烦。我花了几个星期的时间思考解决这个问题的方法,但到目前为止还没有成功。
一些上下文-这是我正在处理的tcp服务器的一部分。tcp服务器的行为类似于websocket规范—当新的客户机连接时,客户机套接字的所有管理都会被抽象掉,相反,简单的回调会被公开,可以向订阅者注册。
即:onreceived、onconnect、ondisconnect、onsend
通过这种方式,我可以让另一个类订阅socket客户机所需的行为,并将管理socket和业务逻辑的职责分开。每当客户端套接字接收到数据时,它都会读取整个数据块,然后引发“onreceived”回调方法,传入接收到的数据。
所有这些的目的都是读取来自lua客户机的各种请求并执行适当的操作。有些操作将转到mysql数据库(通过调用存储过程并将请求数据作为参数传递给存储过程)。此外,还增加了灵活性,可以在请求中发送多个数据集,这意味着在客户端和服务器之间需要发送的请求/响应更少。
这已经好几个月了,使得维护和更改业务代码变得非常容易,而不必担心破坏套接字行为。如果需要更改数据库中的存储过程,只需要更新数据库和lua客户机代码,因为tcp服务器不关心这些实现细节。
然而,现在有一个新的需求,即同时支持mysql和redis,并且tcp服务器能够以灵活的方式将请求定向到这些源中的任何一个。
复制的第一个问题是protocolresponsesingledata和protocolresponsemultidata结构-除了'data'属性之外,这些结构几乎是相同的。我需要能够返回单个数据集或数据集的集合。这两个类都序列化为json字符串。这导致sendtobsingledata和sendtobmultidata方法重复。

struct ProtocolResponseMultiData
{
    public string Action;
    public bool Result;
    public string Error;
    public List<List<object>> Data;
}

struct ProtocolResponseSingleData
{
    public string Action;
    public bool Result;
    public string Error;
    public List<object> Data;
}

第二个问题是有4种方法在方式上非常相似—sendtomysqldbingledata、sendtomysqldbmultidat、sendtoredisdbsingledata、sendtoredisdbmultidata。我似乎不知道如何将这些方法最多折叠成1或2个方法。
第三个问题是这个类都是静态方法—套接字客户机公开回调,但它不知道对象的任何特定示例,因此回调需要声明为静态的。这使得很难应用策略和工厂模式之类的东西来简化设计。
我还有希望吗?

public static void OnReceived(object sender, IPCReceivedEventArgs e)
    {
        try
        {
            LogToFile(e.data, ((SocketClient)(sender)).LogFile);
            Console.WriteLine("Received data from client (" + ((SocketClient)(sender)).Address + ")");

            dynamic j = Newtonsoft.Json.JsonConvert.DeserializeObject(e.data);

            // Verify the request format is valid
            if (j["Action"] != null && j["BulkQuery"] != null && j["Data"] != null && j["Destination"] != null)
            {
                ProtocolRequest request = new ProtocolRequest
                {
                    Action = j["Action"],
                    Destination = j["Destination"],
                    IPAddress = ((SocketClient)(sender)).Address,
                    LogPath = ((SocketClient)(sender)).LogFile
                };
                bool isBulkQuery = j["BulkQuery"];
                string jsonResp = "";
                if (isBulkQuery)
                {
                    ProtocolResponseMultiData resp = SendToDBMultiData(request, ref j);
                    jsonResp = JsonConvert.SerializeObject(resp);
                }
                else
                {
                    ProtocolResponseSingleData resp = SendToDBSingleData(request, ref j);
                    jsonResp = JsonConvert.SerializeObject(resp);
                }            
                ((SocketClient)(sender)).Write(jsonResp);
            }
            else
            {
                // send malformed request response
                string jsonResponse = "{ \"Action\" : " + j["Action"] + ", \"Result\" : false, \"Error\" : \"Malformed Request Received\", \"Data\" : null }";
                ((SocketClient)(sender)).Write(jsonResponse);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception encountered during OnReceived handler: " + ex.Message);
            LogToFile("Exception encountered during OnReceived handler: " + ex.Message, ((SocketClient)(sender)).LogFile);
            string jsonResponse = "{ \"Action\" : \"UNKNOWN\", \"Result\" : false, \"Error\" : \"Malformed JSON Request Received\", \"Data\" : null }";
            ((SocketClient)(sender)).Write(jsonResponse);
        }
        finally
        {
        }
    }

    public static ProtocolResponseSingleData SendToDBSingleData(ProtocolRequest request, ref dynamic j)
    {
        if (request.Destination == "MYSQL")
        {
            return SendToMySQLDBSingleData(request, ref j);
        }
        else if (request.Destination == "REDIS")
        {
            return SendToRedisDBSingleData(request, ref j);
        }
        else
        {
            ProtocolResponseSingleData response = new ProtocolResponseSingleData
            {
                Action = request.Action,
                Error = "Invalid Destination specified - must be either 'REDIS' or 'MYSQL'",
                Data = null,
                Result = false
            };
            return response;
        }
    }

    public static ProtocolResponseMultiData SendToDBMultiData(ProtocolRequest request, ref dynamic j)
    {
        if (request.Destination == "MYSQL")
        {
            return SendToMySQLDBMultiData(request, ref j);
        }
        else if (request.Destination == "REDIS")
        {
            return SendToRedisDBMultiData(request, ref j);
        }
        else
        {
            ProtocolResponseMultiData response = new ProtocolResponseMultiData
            {
                Action = request.Action,
                Error = "Invalid Destination specified - must be either 'REDIS' or 'MYSQL'",
                Data = null,
                Result = false
            };
            return response;
        }
    }

    private static ProtocolResponseSingleData SendToMySQLDBSingleData(ProtocolRequest request, ref dynamic j)
    {
        // serialize a new json string for just the data by itself
        string jdataString = Newtonsoft.Json.JsonConvert.SerializeObject(j["Data"]);
        // now deserialize this string into a list of dictionaries for parsing
        Dictionary<string, object> dataDictionary = null;

        if (((JToken)j["Data"]).Type == JTokenType.Object)
            dataDictionary = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(jdataString);
        else
            dataDictionary = new Dictionary<string, object>();

        ProtocolResponseSingleData result = new ProtocolResponseSingleData
        {
            Action = request.Action,
            Error = "",
            Data = new List<object>(),
            Result = false
        };

        // special scenario - because we cant get the ip address of the game server from DCS, we'll get it from the socket sender object
        // and specially insert it as a parameter into the data dictionary
        // the other special scenario is the server description request can supply - this can contain harmful html, so we must sanitize the input
        if (request.Action == ACTION_GET_SERVERID)
        {
            dataDictionary.Add("IP", request.IPAddress);
            if (dataDictionary.ContainsKey("Description"))
            {
                try
                {
                    string html = Convert.ToString(dataDictionary["Description"]);
                    html = System.Web.HttpUtility.HtmlEncode(html);
                    dataDictionary["Description"] = SanitizeHTML(html);
                }
                catch (Exception ex)
                {
                    LogToFile("Error sanitizing ServerDescription html string (Action: " + request.Action + ") - " + ex.Message, request.LogPath);
                    result.Error = "Error sanitizing ServerDescription html string (Action: " + request.Action + ") - " + ex.Message;
                    return result;
                }
            }
        }    

        MySql.Data.MySqlClient.MySqlConnection _conn = null;
        MySql.Data.MySqlClient.MySqlDataReader rdr = null;

        try
        {
            _conn = new MySql.Data.MySqlClient.MySqlConnection(Config.MySQLDBConnect);
            _conn.Open();
            MySql.Data.MySqlClient.MySqlCommand cmd = new MySql.Data.MySqlClient.MySqlCommand(request.Action)
            {
                Connection = _conn,
                CommandType = System.Data.CommandType.StoredProcedure
            };
            foreach (var d in dataDictionary)
            {
                if (d.Value.GetType() == typeof(Int64) && (Int64)d.Value == LUANULL)
                    cmd.Parameters.AddWithValue(d.Key, null);
                else
                    cmd.Parameters.AddWithValue(d.Key, d.Value);
            }
            rdr = cmd.ExecuteReader();
            if (rdr.Read())
            {
                for (int i = 0; i < rdr.FieldCount; i++)
                {
                    result.Data.Add(rdr[i]);
                }
            }

            rdr.Close();
            _conn.Close();
            result.Result = true;
        }
        catch (Exception ex)
        {
            LogToFile("Error executing query against MySQL (Action: " + request.Action + ") - " + ex.Message, request.LogPath);
            result.Error = "Error executing query against MySQL (Action: " + request.Action + ") - " + ex.Message;
        }
        finally
        {
            if (_conn != null)
                if (_conn.State == System.Data.ConnectionState.Open || _conn.State == System.Data.ConnectionState.Connecting)
                    _conn.Close();

            if (rdr != null)
                if (!rdr.IsClosed)
                    rdr.Close();
        }

        return result;

    }

    private static ProtocolResponseMultiData SendToMySQLDBMultiData(ProtocolRequest request, ref dynamic j)
    {

        // serialize a new json string for just the data by itself
        string jdataString = Newtonsoft.Json.JsonConvert.SerializeObject(j["Data"]);
        // now deserialize this string into a list of dictionaries for parsing
        List<Dictionary<string, object>> dataDictionary =
            Newtonsoft.Json.JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(jdataString);

        ProtocolResponseMultiData result = new ProtocolResponseMultiData
        {
            Action = request.Action,
            Error = "",
            Data = new List<List<object>>(),
            Result = false
        };

        MySql.Data.MySqlClient.MySqlConnection _conn = null;
        MySql.Data.MySqlClient.MySqlDataReader rdr = null;

        try
        {
            foreach (var d in dataDictionary)
            {
                _conn = new MySql.Data.MySqlClient.MySqlConnection(Config.MySQLDBConnect);
                _conn.Open();
                MySql.Data.MySqlClient.MySqlCommand cmd = new MySql.Data.MySqlClient.MySqlCommand(request.Action)
                {
                    Connection = _conn,
                    CommandType = System.Data.CommandType.StoredProcedure
                };
                foreach (var kv in d)
                {
                    if (kv.Value.GetType() == typeof(Int64) && (Int64)kv.Value == LUANULL)
                        cmd.Parameters.AddWithValue(kv.Key, null);
                    else
                        cmd.Parameters.AddWithValue(kv.Key, kv.Value);
                }
                rdr = cmd.ExecuteReader();
                if (rdr.Read())
                {
                    List<object> result_set = new List<object>();
                    for (int i = 0; i < rdr.FieldCount; i++)
                    {
                        result_set.Add(rdr[i]);
                    }
                    result.Data.Add(result_set);
                }
                else
                {
                    result.Error += "No Results Returned\n";
                }
                rdr.Close();
                _conn.Close();
            }
            result.Result = true;
        }
        catch (Exception ex)
        {
            LogToFile("Error executing query against MySQL (Action: " + request.Action + ") - " + ex.Message, request.LogPath);
            result.Error = "Error executing query against MySQL (Action: " + request.Action + ") - " + ex.Message;
        }
        finally
        {
            if (_conn != null)
                if (_conn.State == System.Data.ConnectionState.Open || _conn.State == System.Data.ConnectionState.Connecting)
                    _conn.Close();

            if (rdr != null)
                if (!rdr.IsClosed)
                    rdr.Close();
        }

        return result;
    }

    private static ProtocolResponseSingleData SendToRedisDBSingleData(ProtocolRequest request, ref dynamic j)
    {
        // Serialize the JSON Data property into its own JSON String
        string jdataString = Newtonsoft.Json.JsonConvert.SerializeObject(j["Data"]);

        // now deserialize this string into a list of dictionaries for parsing
        Dictionary<string, object> dataDictionary = null;

        if (((JToken)j["Data"]).Type == JTokenType.Object)
            dataDictionary = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, object>>(jdataString);
        else
            dataDictionary = new Dictionary<string, object>();

        ProtocolResponseSingleData result = new ProtocolResponseSingleData
        {
            Action = request.Action,
            Error = "",
            Data = new List<object>(),
            Result = false
        };

        if (!RedisConnection.IsConnected)
        {
            LogToFile("Connection to Redis Closed - Attempting to reopen...", request.LogPath);
            try
            {
                RedisConnection = ConnectionMultiplexer.Connect(Config.RedisDBConnect);
            }
            catch (Exception ex)
            {
                LogToFile("Error connecting to Redis - lost connection (" + ex.Message + ")", request.LogPath);
                result.Error = "Error connecting to Redis - lost connection (" + ex.Message + ")";
                return result;
            }
        }

        try
        {
            string serverid = Convert.ToString(dataDictionary["ServerID"]);
            string rediskey = Config.RedisActionKeys[request.Action];

            if (serverid == null)
            {
                result.Error = "Error executing query against Redis (Action: " + request.Action + ") - " + "'ServerID' not found in Data request";
                return result;
            }

            if (rediskey == null)
            {
                result.Error = "Error executing query against Redis - Action: '" + request.Action + "' not found in server configuration - please check action message or server configuration.";
                return result;
            }

            IDatabase db = RedisConnection.GetDatabase();
            string k = rediskey + ":" + serverid;
            if (!db.StringSet(k, jdataString))
            {
                result.Error = "Failed to Set Key in Redis (Key: '" + k + "')";
            }
            else
            {
                result.Data.Add(1);
                result.Result = true;
            }
        }
        catch (Exception ex)
        {
            LogToFile("Error executing query against Redis (Action: " + request.Action + ") - " + ex.Message, request.LogPath);
            result.Error = "Error executing query against Redis (Action: " + request.Action + ") - " + ex.Message;
        }         

        return result;
    }

    private static ProtocolResponseMultiData SendToRedisDBMultiData(ProtocolRequest request, ref dynamic j)
    {
        // serialize a new json string for just the data by itself
        string jdataString = Newtonsoft.Json.JsonConvert.SerializeObject(j["Data"]);
        // now deserialize this string into a list of dictionaries for parsing
        List<Dictionary<string, object>> dataDictionary =
            Newtonsoft.Json.JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(jdataString);

        ProtocolResponseMultiData result = new ProtocolResponseMultiData
        {
            Action = request.Action,
            Error = "",
            Data = new List<List<object>>(),
            Result = false
        };

        if (!RedisConnection.IsConnected)
        {
            LogToFile("Connection to Redis Closed - Attempting to reopen...", request.LogPath);
            try
            {
                RedisConnection = ConnectionMultiplexer.Connect(Config.RedisDBConnect);
            }
            catch (Exception ex)
            {
                LogToFile("Error connecting to Redis - lost connection (" + ex.Message + ")", request.LogPath);
                result.Error = "Error connecting to Redis - lost connection (" + ex.Message + ")";
                return result;
            }
        }

        try
        {
            int id = 0;
            foreach (Dictionary<string, object> x in dataDictionary)
            {
                id += 1;
                string serverid = Convert.ToString(x["ServerID"]);
                string rediskey = Config.RedisActionKeys[request.Action];

                if (serverid == null)
                {
                    result.Error = "Error executing query against Redis (Action: " + request.Action + ") - " + "'ServerID' not found in Data request";
                    return result;
                }

                if (rediskey == null)
                {
                    result.Error = "Error executing query against Redis - Action: '" + request.Action + "' not found in server configuration - please check action message or server configuration.";
                    return result;
                }

                IDatabase db = RedisConnection.GetDatabase();
                string k = rediskey + ":" + serverid + ":" + id;
                string jdatastring = Newtonsoft.Json.JsonConvert.SerializeObject(x);
                if (!db.StringSet(k, jdatastring))
                {
                    result.Error = "Failed to Set Key in Redis (Key: '" + k + "')";
                    result.Result = false;
                }
                else
                {
                    List<object> res = new List<object>
                    {
                        k
                    };
                    result.Data.Add(res);
                    result.Result = true;
                }
            }    
        }
        catch (Exception ex)
        {
            LogToFile("Error executing query against Redis (Action: " + request.Action + ") - " + ex.Message, request.LogPath);
            result.Error = "Error executing query against Redis (Action: " + request.Action + ") - " + ex.Message;
        }

        return result;
    }

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题