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

时间:2018-04-19 04:54:22

标签: c# mysql sockets lua redis

下面是一段代码,它是静态类的一部分,让我无法进行重构。我花了几个星期思考如何处理这个问题,但到目前为止还没有成功。

某些上下文 - 这是我正在处理的TCP服务器的一部分。 TCP服务器的行为类似于websocket规范 - 当新客户端连接时,客户端套接字的所有管理都被抽象掉了,而是公开了可以注册给订户的简单回调。

即。 OnReceived,OnConnect,OnDisconnect,OnSend

这样我就可以让另一个类订阅套接字客户端所需的行为,并将管理套接字和业务逻辑的职责分开。只要客户端套接字接收数据,它就会读取整个数据块,然后引发“OnReceived”回调方法,传入接收到的数据。

所有这一切的目的是从Lua客户端读取各种请求并执行适当的操作。一些操作将转到MySQL数据库(通过调用存储过程并将请求数据作为参数传递给sproc)。在请求中能够发送多个数据集的灵活性也增加了,这意味着需要在客户端和服务器之间发送更少的请求/响应。

这已经好几个月了,并且使得维护和更改业务代码变得非常容易,而无需担心破坏套接字行为。如果我需要更改数据库中的存储过程,则只需要更新数据库和Lua客户端代码,因为TCP服务器不关心这些实现细节。

但是,现在有一个新的要求是支持MySQL和Redis,并且TCP服务器能够以灵活的方式将请求引导到这些源中的任何一个。

重复的第一个问题 ProtocolResponseSingleData ProtocolResponseMultiData 结构 - 这些结构几乎完全相同,但“数据”属性除外。我需要能够返回单个数据集或数据集的集合。这两个类都被序列化为JSON字符串。这导致了 SendToDBSingleData SendToDBMultiData 方法的重复。

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种方法在方式上非常相似 - SendToMySQLDBSingleData,SendToMySQLDBMultiData,SendToRedisDBSingleData,SendToRedisDBMultiData。我似乎无法弄清楚如何将这些方法最多折叠为1或2种方法。

第三个问题这个类是所有静态方法 - 套接字客户端公开回调,但它不知道对象的任何特定实例,因此回调需要声明为static。这使得很难应用策略和工厂模式之类的东西来简化设计。

我有希望吗?

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;
    }

0 个答案:

没有答案