Use new HttpClient class for most web calls
This commit is contained in:
parent
7342aee296
commit
41561261f7
|
@ -3,7 +3,9 @@ using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Core.Main;
|
using Core.Main;
|
||||||
using Core.Helper;
|
using Core.Helper;
|
||||||
|
@ -13,190 +15,119 @@ namespace Core.MarketAnalyzer
|
||||||
{
|
{
|
||||||
public class BaseAnalyzer
|
public class BaseAnalyzer
|
||||||
{
|
{
|
||||||
public static Dictionary<string, dynamic> GetJsonFromURL(string url, LogHelper log, string api)
|
public static string GetJsonStringFromURL(string url, LogHelper log, (string header, string value)[] headers = null)
|
||||||
{
|
{
|
||||||
Dictionary<string, dynamic> jsonObject = null;
|
HttpClient webClient = null;
|
||||||
|
|
||||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
if (webClient == null)
|
||||||
if (api != "")
|
|
||||||
{
|
{
|
||||||
request.Headers.Add("X-CMC_PRO_API_KEY", api);
|
webClient = new HttpClient();
|
||||||
|
|
||||||
|
// Setup the one time conneciton characteristics
|
||||||
|
webClient.Timeout = new TimeSpan(0, 0, 30); // 30 second call timeout
|
||||||
|
webClient.DefaultRequestHeaders.ConnectionClose = false; // Keep alives
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
webClient.DefaultRequestHeaders.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
request.ContentType = "application/json";
|
// Accept JSON and Text
|
||||||
request.UserAgent = "PTMagic.Import";
|
webClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
request.KeepAlive = true;
|
webClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain"));
|
||||||
request.Timeout = 10000;
|
|
||||||
|
|
||||||
HttpWebResponse httpResponse = null;
|
// Setup the keep alive timeout
|
||||||
string jsonString = string.Empty;
|
ServicePointManager.FindServicePoint(new Uri(url)).ConnectionLeaseTimeout = 300000; // 5 mins for keep alives
|
||||||
|
|
||||||
|
// Add any custom headers
|
||||||
|
if (headers != null)
|
||||||
|
{
|
||||||
|
foreach (var header in headers)
|
||||||
|
{
|
||||||
|
webClient.DefaultRequestHeaders.Add(header.header, header.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
log.DoLogInfo("Calling URL: " + url);
|
log.DoLogInfo("Calling URL: " + url);
|
||||||
httpResponse = (HttpWebResponse)request.GetResponse();
|
var response = webClient.GetAsync(url).Result;
|
||||||
|
|
||||||
using (StreamReader jsonReader = new StreamReader(httpResponse.GetResponseStream()))
|
string repsonseString = response.Content.ReadAsStringAsync().Result;
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
jsonString = jsonReader.ReadToEnd();
|
return repsonseString;
|
||||||
jsonReader.Close();
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Error
|
||||||
|
var message = string.Format("Error whilst calling {0} - {1}", url, repsonseString);
|
||||||
|
|
||||||
|
log.DoLogError(message);
|
||||||
|
|
||||||
|
throw new Exception(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException tcEx)
|
||||||
|
{
|
||||||
|
// Conneciton timeout
|
||||||
|
log.DoLogError(string.Format("Timeout whilst calling {0} - {1}", url, tcEx.Message));
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.DoLogError(string.Format("Error whilst calling {0} \nError: {1}", url, ex.Message));
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Dictionary<string, dynamic> GetJsonFromURL(string url, LogHelper log, (string header, string value)[] headers = null)
|
||||||
|
{
|
||||||
|
Dictionary<string, dynamic> jsonObject = null;
|
||||||
|
|
||||||
|
string jsonString = GetJsonStringFromURL(url, log, headers);
|
||||||
|
|
||||||
|
// Convert the response to JSON
|
||||||
jsonObject = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(jsonString);
|
jsonObject = JsonConvert.DeserializeObject<Dictionary<string, dynamic>>(jsonString);
|
||||||
|
|
||||||
return jsonObject;
|
return jsonObject;
|
||||||
}
|
}
|
||||||
catch (WebException ex)
|
|
||||||
{
|
|
||||||
log.DoLogCritical(string.Format("Error whilst calling {0} \nError: {1}", url, ex.Message), ex);
|
|
||||||
|
|
||||||
if (ex.Response != null)
|
public static Newtonsoft.Json.Linq.JObject GetSimpleJsonObjectFromURL(string url, LogHelper log, (string header, string value)[] headers = null)
|
||||||
{
|
|
||||||
// Error calling the service but we got a response so dump it.
|
|
||||||
string responseString = string.Empty;
|
|
||||||
var response = ((HttpWebResponse)ex.Response);
|
|
||||||
var encoding = response.CharacterSet == "" ? Encoding.UTF8 : Encoding.GetEncoding(response.CharacterSet);
|
|
||||||
|
|
||||||
using (var stream = response.GetResponseStream())
|
|
||||||
{
|
|
||||||
var reader = new StreamReader(stream, encoding);
|
|
||||||
responseString = reader.ReadToEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
log.DoLogCritical(String.Format("{0} - Response: ({1}) {2} : {3}", ex.Message, response.StatusCode, response.StatusDescription, responseString), ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
log.DoLogCritical(ex.Message, ex);
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
// Do any necessary clean up.
|
|
||||||
if (httpResponse != null) httpResponse.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Newtonsoft.Json.Linq.JObject GetSimpleJsonObjectFromURL(string url, LogHelper log, bool swallowException)
|
|
||||||
{
|
{
|
||||||
Newtonsoft.Json.Linq.JObject jsonObject = null;
|
Newtonsoft.Json.Linq.JObject jsonObject = null;
|
||||||
|
|
||||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
string jsonString = GetJsonStringFromURL(url, log, headers);
|
||||||
request.ContentType = "application/json";
|
|
||||||
request.UserAgent = "PTMagic.Import";
|
|
||||||
request.KeepAlive = true;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
HttpWebResponse httpResponse = (HttpWebResponse)request.GetResponse();
|
|
||||||
|
|
||||||
StreamReader jsonReader = new StreamReader(httpResponse.GetResponseStream());
|
|
||||||
string jsonString = jsonReader.ReadToEnd();
|
|
||||||
jsonReader.Close();
|
|
||||||
|
|
||||||
jsonObject = JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(jsonString);
|
jsonObject = JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(jsonString);
|
||||||
|
|
||||||
return jsonObject;
|
return jsonObject;
|
||||||
}
|
}
|
||||||
catch (WebException ex)
|
|
||||||
{
|
|
||||||
if (swallowException)
|
|
||||||
{
|
|
||||||
// Do nothing, as we do not want to get this logged. Only uncritical functions uses this
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
log.DoLogCritical("Url: " + url + " Message: " + ex.Message, ex);
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (swallowException)
|
|
||||||
{
|
|
||||||
// Do nothing, as we do not want to get this logged. Only uncritical functions uses this
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
log.DoLogCritical("Url: " + url + " Message: " + ex.Message, ex);
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<dynamic> GetSimpleJsonListFromURL(string url, LogHelper log)
|
public static List<dynamic> GetSimpleJsonListFromURL(string url, LogHelper log)
|
||||||
{
|
{
|
||||||
List<dynamic> jsonObject = null;
|
List<dynamic> jsonObject = null;
|
||||||
|
|
||||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
string jsonString = GetJsonStringFromURL(url, log, null);
|
||||||
request.ContentType = "application/json";
|
|
||||||
request.UserAgent = "PTMagic.Import";
|
|
||||||
request.KeepAlive = true;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
HttpWebResponse httpResponse = (HttpWebResponse)request.GetResponse();
|
|
||||||
|
|
||||||
StreamReader jsonReader = new StreamReader(httpResponse.GetResponseStream());
|
|
||||||
string jsonString = jsonReader.ReadToEnd();
|
|
||||||
jsonReader.Close();
|
|
||||||
|
|
||||||
jsonObject = JsonConvert.DeserializeObject<List<dynamic>>(jsonString);
|
jsonObject = JsonConvert.DeserializeObject<List<dynamic>>(jsonString);
|
||||||
|
|
||||||
return jsonObject;
|
return jsonObject;
|
||||||
}
|
}
|
||||||
catch (WebException ex)
|
|
||||||
{
|
|
||||||
log.DoLogCritical(ex.Message, ex);
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
log.DoLogCritical(ex.Message, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Newtonsoft.Json.Linq.JArray GetSimpleJsonArrayFromURL(string url, LogHelper log)
|
public static Newtonsoft.Json.Linq.JArray GetSimpleJsonArrayFromURL(string url, LogHelper log)
|
||||||
{
|
{
|
||||||
Newtonsoft.Json.Linq.JArray jsonObject = null;
|
Newtonsoft.Json.Linq.JArray jsonObject = null;
|
||||||
|
|
||||||
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
|
string jsonString = GetJsonStringFromURL(url, log, null);
|
||||||
request.ContentType = "application/json";
|
|
||||||
request.UserAgent = "PTMagic.Import";
|
|
||||||
request.KeepAlive = true;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
HttpWebResponse httpResponse = (HttpWebResponse)request.GetResponse();
|
|
||||||
|
|
||||||
StreamReader jsonReader = new StreamReader(httpResponse.GetResponseStream());
|
|
||||||
string jsonString = jsonReader.ReadToEnd();
|
|
||||||
jsonReader.Close();
|
|
||||||
|
|
||||||
jsonObject = JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JArray>(jsonString);
|
jsonObject = JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JArray>(jsonString);
|
||||||
|
|
||||||
return jsonObject;
|
return jsonObject;
|
||||||
}
|
}
|
||||||
catch (WebException ex)
|
|
||||||
{
|
|
||||||
log.DoLogCritical(ex.Message, ex);
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
log.DoLogCritical(ex.Message, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetLatestGitHubRelease(LogHelper log, string defaultVersion)
|
public static string GetLatestGitHubRelease(LogHelper log, string defaultVersion)
|
||||||
{
|
{
|
||||||
|
@ -206,7 +137,8 @@ namespace Core.MarketAnalyzer
|
||||||
{
|
{
|
||||||
string baseUrl = "https://api.github.com/repos/PTMagicians/PTMagic/releases/latest";
|
string baseUrl = "https://api.github.com/repos/PTMagicians/PTMagic/releases/latest";
|
||||||
|
|
||||||
Newtonsoft.Json.Linq.JObject jsonObject = GetSimpleJsonObjectFromURL(baseUrl, log, true);
|
Newtonsoft.Json.Linq.JObject jsonObject = GetSimpleJsonObjectFromURL(baseUrl, log, new (string header, string value)[] { ("User-Agent", "PTMagic.Import") });
|
||||||
|
|
||||||
if (jsonObject != null)
|
if (jsonObject != null)
|
||||||
{
|
{
|
||||||
result = jsonObject.GetValue("tag_name").ToString();
|
result = jsonObject.GetValue("tag_name").ToString();
|
||||||
|
@ -231,7 +163,7 @@ namespace Core.MarketAnalyzer
|
||||||
string baseUrl = "http://free.currencyconverterapi.com/api/v5/convert?q=USD_" + currency + "&compact=y&apiKey=" + FreeCurrencyAPI;
|
string baseUrl = "http://free.currencyconverterapi.com/api/v5/convert?q=USD_" + currency + "&compact=y&apiKey=" + FreeCurrencyAPI;
|
||||||
|
|
||||||
log.DoLogDebug("http://free.currencyconverterapi.com - Getting latest exchange rates...");
|
log.DoLogDebug("http://free.currencyconverterapi.com - Getting latest exchange rates...");
|
||||||
Newtonsoft.Json.Linq.JObject jsonObject = GetSimpleJsonObjectFromURL(baseUrl, log, false);
|
Newtonsoft.Json.Linq.JObject jsonObject = GetSimpleJsonObjectFromURL(baseUrl, log, null);
|
||||||
if (jsonObject != null)
|
if (jsonObject != null)
|
||||||
{
|
{
|
||||||
log.DoLogDebug("http://free.currencyconverterapi.com - Received latest exchange rates.");
|
log.DoLogDebug("http://free.currencyconverterapi.com - Received latest exchange rates.");
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace Core.MarketAnalyzer
|
||||||
string baseUrl = "https://api.binance.com/api/v1/ticker/24hr?symbol=" + mainMarket + "USDT";
|
string baseUrl = "https://api.binance.com/api/v1/ticker/24hr?symbol=" + mainMarket + "USDT";
|
||||||
|
|
||||||
log.DoLogInfo("Binance - Getting main market price...");
|
log.DoLogInfo("Binance - Getting main market price...");
|
||||||
Newtonsoft.Json.Linq.JObject jsonObject = GetSimpleJsonObjectFromURL(baseUrl, log, false);
|
Newtonsoft.Json.Linq.JObject jsonObject = GetSimpleJsonObjectFromURL(baseUrl, log, null);
|
||||||
if (jsonObject != null)
|
if (jsonObject != null)
|
||||||
{
|
{
|
||||||
log.DoLogInfo("Binance - Market data received for " + mainMarket + "USDT");
|
log.DoLogInfo("Binance - Market data received for " + mainMarket + "USDT");
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace Core.MarketAnalyzer
|
||||||
string baseUrl = "https://api.binance.us/api/v1/ticker/24hr?symbol=" + mainMarket + "USDT";
|
string baseUrl = "https://api.binance.us/api/v1/ticker/24hr?symbol=" + mainMarket + "USDT";
|
||||||
|
|
||||||
log.DoLogInfo("BinanceUS - Getting main market price...");
|
log.DoLogInfo("BinanceUS - Getting main market price...");
|
||||||
Newtonsoft.Json.Linq.JObject jsonObject = GetSimpleJsonObjectFromURL(baseUrl, log, false);
|
Newtonsoft.Json.Linq.JObject jsonObject = GetSimpleJsonObjectFromURL(baseUrl, log, null);
|
||||||
if (jsonObject != null)
|
if (jsonObject != null)
|
||||||
{
|
{
|
||||||
log.DoLogInfo("BinanceUS - Market data received for " + mainMarket + "USDT");
|
log.DoLogInfo("BinanceUS - Market data received for " + mainMarket + "USDT");
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace Core.MarketAnalyzer
|
||||||
string baseUrl = "https://bittrex.com/api/v1.1/public/getmarketsummary?market=USDT-" + mainMarket;
|
string baseUrl = "https://bittrex.com/api/v1.1/public/getmarketsummary?market=USDT-" + mainMarket;
|
||||||
|
|
||||||
log.DoLogInfo("Bittrex - Getting main market price...");
|
log.DoLogInfo("Bittrex - Getting main market price...");
|
||||||
Dictionary<string, dynamic> jsonObject = GetJsonFromURL(baseUrl, log, "");
|
Dictionary<string, dynamic> jsonObject = GetJsonFromURL(baseUrl, log, null);
|
||||||
if (jsonObject.Count > 0)
|
if (jsonObject.Count > 0)
|
||||||
{
|
{
|
||||||
if (jsonObject["success"])
|
if (jsonObject["success"])
|
||||||
|
@ -55,7 +55,7 @@ namespace Core.MarketAnalyzer
|
||||||
string baseUrl = "https://bittrex.com/api/v2.0/pub/markets/GetMarketSummaries";
|
string baseUrl = "https://bittrex.com/api/v2.0/pub/markets/GetMarketSummaries";
|
||||||
|
|
||||||
log.DoLogInfo("Bittrex - Getting market data...");
|
log.DoLogInfo("Bittrex - Getting market data...");
|
||||||
Dictionary<string, dynamic> jsonObject = GetJsonFromURL(baseUrl, log, "");
|
Dictionary<string, dynamic> jsonObject = GetJsonFromURL(baseUrl, log, null);
|
||||||
if (jsonObject.Count > 0)
|
if (jsonObject.Count > 0)
|
||||||
{
|
{
|
||||||
if (jsonObject["success"])
|
if (jsonObject["success"])
|
||||||
|
@ -150,7 +150,7 @@ namespace Core.MarketAnalyzer
|
||||||
string baseUrl = "https://bittrex.com/Api/v2.0/pub/market/GetTicks?tickInterval=oneMin&marketName=" + marketName;
|
string baseUrl = "https://bittrex.com/Api/v2.0/pub/market/GetTicks?tickInterval=oneMin&marketName=" + marketName;
|
||||||
|
|
||||||
log.DoLogDebug("Bittrex - Getting ticks for '" + marketName + "'...");
|
log.DoLogDebug("Bittrex - Getting ticks for '" + marketName + "'...");
|
||||||
Dictionary<string, dynamic> jsonObject = GetJsonFromURL(baseUrl, log, "");
|
Dictionary<string, dynamic> jsonObject = GetJsonFromURL(baseUrl, log, null);
|
||||||
if (jsonObject.Count > 0)
|
if (jsonObject.Count > 0)
|
||||||
{
|
{
|
||||||
if (jsonObject["success"])
|
if (jsonObject["success"])
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace Core.MarketAnalyzer
|
||||||
|
|
||||||
log.DoLogInfo("CoinMarketCap - Getting market data...");
|
log.DoLogInfo("CoinMarketCap - Getting market data...");
|
||||||
|
|
||||||
Dictionary<string, dynamic> jsonObject = GetJsonFromURL(baseUrl, log, cmcAPI);
|
Dictionary<string, dynamic> jsonObject = GetJsonFromURL(baseUrl, log, new (string header, string value)[] { ("X-CMC_PRO_API_KEY", cmcAPI) });
|
||||||
|
|
||||||
if (jsonObject.Count > 0)
|
if (jsonObject.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace Core.MarketAnalyzer
|
||||||
string baseUrl = "https://bittrex.com/api/v1.1/public/getmarketsummary?market=USDT-" + mainMarket;
|
string baseUrl = "https://bittrex.com/api/v1.1/public/getmarketsummary?market=USDT-" + mainMarket;
|
||||||
|
|
||||||
log.DoLogInfo("Poloniex - Getting main market price...");
|
log.DoLogInfo("Poloniex - Getting main market price...");
|
||||||
Dictionary<string, dynamic> jsonObject = GetJsonFromURL(baseUrl, log, "");
|
Dictionary<string, dynamic> jsonObject = GetJsonFromURL(baseUrl, log, null);
|
||||||
if (jsonObject.Count > 0)
|
if (jsonObject.Count > 0)
|
||||||
{
|
{
|
||||||
if (jsonObject["success"])
|
if (jsonObject["success"])
|
||||||
|
@ -55,7 +55,7 @@ namespace Core.MarketAnalyzer
|
||||||
string baseUrl = "https://poloniex.com/public?command=returnTicker";
|
string baseUrl = "https://poloniex.com/public?command=returnTicker";
|
||||||
|
|
||||||
log.DoLogInfo("Poloniex - Getting market data...");
|
log.DoLogInfo("Poloniex - Getting market data...");
|
||||||
Dictionary<string, dynamic> jsonObject = GetJsonFromURL(baseUrl, log, "");
|
Dictionary<string, dynamic> jsonObject = GetJsonFromURL(baseUrl, log, null);
|
||||||
if (jsonObject.Count > 0)
|
if (jsonObject.Count > 0)
|
||||||
{
|
{
|
||||||
log.DoLogInfo("Poloniex - Market data received for " + jsonObject.Count.ToString() + " currencies");
|
log.DoLogInfo("Poloniex - Market data received for " + jsonObject.Count.ToString() + " currencies");
|
||||||
|
|
|
@ -51,12 +51,14 @@ namespace Core.ProfitTrailer
|
||||||
log.DoLogDebug("Built POST request for Properties");
|
log.DoLogDebug("Built POST request for Properties");
|
||||||
|
|
||||||
log.DoLogInfo("Sending Properties...");
|
log.DoLogInfo("Sending Properties...");
|
||||||
HttpWebResponse httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
|
using (HttpWebResponse httpResponse = (HttpWebResponse)httpWebRequest.GetResponse())
|
||||||
|
{
|
||||||
log.DoLogInfo("Properties sent!");
|
log.DoLogInfo("Properties sent!");
|
||||||
httpResponse.Close();
|
httpResponse.Close();
|
||||||
log.DoLogDebug("Properties response object closed.");
|
log.DoLogDebug("Properties response object closed.");
|
||||||
transferCompleted = true;
|
}
|
||||||
|
|
||||||
|
transferCompleted = true;
|
||||||
}
|
}
|
||||||
catch (WebException ex)
|
catch (WebException ex)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue