diff --git a/ReadMe.md b/ReadMe.md
index 54720a4..66459ec 100644
--- a/ReadMe.md
+++ b/ReadMe.md
@@ -5,16 +5,20 @@
### Modules
* NB All projects upgraded to .Net 6, 28Nov23.
-* SACS Standard Base
- * Base class library. .Net Standard 2.1. C#.
-* SACS Standard Dbas
- * Base database interface library. .Net Standard 2.1. C#.
-* SACS Standard Accounts
- * Base Acounts system interface library. .Net Standard 2.1. C#.
-* SACS Standard Mob
- * Base Mobile application library. .Net Standard 2.1. C#.
-* SACS Core Web
- * Base Web application library. [C# .Net 6]
-* SACS Core Unit Tests
- * Unit Tests. [C# .Net 6]
+* **SACS Base**
+ * C# Base class library, .Net6 (was .Net Standard 2.1).
+* **SACS Dbas**
+ * C# Base database interface library, .Net6 (was .Net Standard 2.1).
+* **SACS Proc**
+ * C# Base processes library, .Net6.
+* **SACS Maui**
+ * C# Maui Mobile library, .Net6 (added Apr24).
+* **SACS Xamarin** (Apr24 SACS Standard Mob)
+ * C# Xamarin Mobile library, .Net6 (was .Net Standard 2.1).
+* **SACS Web**
+ * C# Base Web library, .Net 6.
+* **SACS Accounts**
+ * C# Base Accounts system interface library, .Net6 (was .Net Standard 2.1).
+* **SACS Unit Tests**
+ * C# Unit Tests, .Net 6.
diff --git a/SacsCoreUnitTests/SacsCoreUnitTests.csproj b/SacsCoreUnitTests/SacsCoreUnitTests.csproj
index 8d897ad..63a75b7 100644
--- a/SacsCoreUnitTests/SacsCoreUnitTests.csproj
+++ b/SacsCoreUnitTests/SacsCoreUnitTests.csproj
@@ -19,6 +19,7 @@
+
diff --git a/SacsCoreUnitTests/Standard/Base/Utils/StringUtil_Tester.cs b/SacsCoreUnitTests/Standard/Base/Utils/StringUtil_Tester.cs
index 9e1c803..5b3cf9c 100644
--- a/SacsCoreUnitTests/Standard/Base/Utils/StringUtil_Tester.cs
+++ b/SacsCoreUnitTests/Standard/Base/Utils/StringUtil_Tester.cs
@@ -1,4 +1,5 @@
using System;
+using System.Text.RegularExpressions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sarand.Net.Standard.Base.Utils;
@@ -43,6 +44,61 @@ namespace Sarand.UnitTests.Core.Base.Utils
Assert.AreEqual(false, "Abc".IsBlank());
}
+ //-------------------------------------------------------------------
+ // Regular Expression Matching.
+ //-------------------------------------------------------------------
+ [TestMethod]
+ public void StringUtil_Match()
+ {
+ // Match.
+ Assert.AreEqual(true, "ABCDEF".Match("CDE"));
+ Assert.AreEqual(true, "ABCDEF".MatchCI("cDe"));
+ Assert.AreEqual(true, "ABCDEF".Match("CDE", System.Text.RegularExpressions.RegexOptions.None));
+ Assert.AreEqual(true, "ABCDEF".Match(new Regex("CDE", System.Text.RegularExpressions.RegexOptions.None)));
+ Assert.AreEqual(true, "ABCDEF".Match(new Regex("cDe", System.Text.RegularExpressions.RegexOptions.IgnoreCase)));
+
+ // Match with escaped content.
+ Assert.AreEqual(true, "ABC\\sDEF".Match("\\\\s"));
+ Assert.AreEqual(true, "AB(CD)EF".Match("(..)"));
+ Assert.AreEqual(true, "AB(CD)EF".Match("\\(CD\\)"));
+
+ // Not match.
+ Assert.AreEqual(false, "ABCDEF".Match("cDe"));
+ Assert.AreEqual(false, "ABCDEF".MatchCI("eFg"));
+ Assert.AreEqual(false, "ABCDEF".Match("cDe", System.Text.RegularExpressions.RegexOptions.None));
+ Assert.AreEqual(false, "ABCDEF".Match(new Regex("cDe", System.Text.RegularExpressions.RegexOptions.None)));
+ }
+
+ [TestMethod]
+ public void StringUtil_IsMatch()
+ {
+ // IsMatch.
+ Assert.AreEqual(true, "ABCDEF".IsMatch("CDE"));
+ Assert.AreEqual(true, "ABCDEF".IsMatchCI("cDe"));
+ Assert.AreEqual(true, "ABCDEF".IsMatch("CDE", System.Text.RegularExpressions.RegexOptions.None));
+ Assert.AreEqual(true, "ABCDEF".IsMatch(new Regex("CDE", System.Text.RegularExpressions.RegexOptions.None)));
+ Assert.AreEqual(true, "ABCDEF".IsMatch(new Regex("cDe", System.Text.RegularExpressions.RegexOptions.IgnoreCase)));
+
+ // IsMatch with escaped content.
+ Assert.AreEqual(true, "ABC\\sDEF".IsMatch("\\\\s"));
+ Assert.AreEqual(true, "AB(CD)EF".IsMatch("(..)"));
+ Assert.AreEqual(true, "AB(CD)EF".IsMatch("\\(CD\\)"));
+
+ // Not match.
+ Assert.AreEqual(false, "ABCDEF".IsMatch("cDe"));
+ Assert.AreEqual(false, "ABCDEF".IsMatchCI("eFg"));
+ Assert.AreEqual(false, "ABCDEF".IsMatch("cDe", System.Text.RegularExpressions.RegexOptions.None));
+ Assert.AreEqual(false, "ABCDEF".IsMatch(new Regex("cDe", System.Text.RegularExpressions.RegexOptions.None)));
+ }
+
+ [TestMethod]
+ public void StringUtil_MatchCount()
+ {
+ // Match.
+ Assert.AreEqual(1, "ABCDEF".MatchCount(new Regex("CDE")));
+ Assert.AreEqual(2, "ABCDEFGHICDEJKL".MatchCount(new Regex("CDE")));
+ }
+
//-------------------------------------------------------------------
// Padding.
//-------------------------------------------------------------------
diff --git a/SacsCoreUnitTests/Standard/Proc/Excel/SaMsSpreadsheet_Tester.cs b/SacsCoreUnitTests/Standard/Proc/Excel/SaMsSpreadsheet_Tester.cs
new file mode 100644
index 0000000..35a4839
--- /dev/null
+++ b/SacsCoreUnitTests/Standard/Proc/Excel/SaMsSpreadsheet_Tester.cs
@@ -0,0 +1,42 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Sarand.Net.Standard.Proc.Excel;
+using System;
+
+namespace Sacs.UnitTests.Net.Standard.Proc.Excel
+{
+ [TestClass]
+ public class SaMsSpreadsheet_Tester
+ {
+ //-------------------------------------------------------------------
+ // Data members.
+ private Random random = new Random();
+
+ //-------------------------------------------------------------------
+ // GetPosForColId.
+ //-------------------------------------------------------------------
+ [TestMethod]
+ public void GetPosForColId_IsEmpty()
+ {
+ Assert.AreEqual(1, SaMsSpreadsheet.GetPosForColId("A"));
+ Assert.AreEqual(1, SaMsSpreadsheet.GetPosForColId("a"));
+ Assert.AreEqual(2, SaMsSpreadsheet.GetPosForColId("B"));
+ Assert.AreEqual(3, SaMsSpreadsheet.GetPosForColId("C"));
+ Assert.AreEqual(24, SaMsSpreadsheet.GetPosForColId("X"));
+ Assert.AreEqual(25, SaMsSpreadsheet.GetPosForColId("Y"));
+ Assert.AreEqual(26, SaMsSpreadsheet.GetPosForColId("Z"));
+ Assert.AreEqual(27, SaMsSpreadsheet.GetPosForColId("AA"));
+ Assert.AreEqual(28, SaMsSpreadsheet.GetPosForColId("AB"));
+ Assert.AreEqual(29, SaMsSpreadsheet.GetPosForColId("AC"));
+ Assert.AreEqual(128, SaMsSpreadsheet.GetPosForColId("DX"));
+ Assert.AreEqual(155, SaMsSpreadsheet.GetPosForColId("EY"));
+ Assert.AreEqual(182, SaMsSpreadsheet.GetPosForColId("FZ"));
+
+ // Invalid.
+ Assert.AreEqual(0, SaMsSpreadsheet.GetPosForColId(""));
+ Assert.AreEqual(0, SaMsSpreadsheet.GetPosForColId("1"));
+ Assert.AreEqual(0, SaMsSpreadsheet.GetPosForColId("A1"));
+ }
+
+ //-------------------------------------------------------------------
+ }
+}
diff --git a/SacsCoreWeb/Web/Components/SaController.cs b/SacsCoreWeb/Web/Components/SaController.cs
index a31853e..b518979 100644
--- a/SacsCoreWeb/Web/Components/SaController.cs
+++ b/SacsCoreWeb/Web/Components/SaController.cs
@@ -141,7 +141,7 @@ namespace Sacs.Web.Components
//if (SacsSystem.DebugMode) SaLogger.Debug(SysUtil.Fn + "\n\tData: " + queryString + " \n\tEncrypted: " + encrypted);
return encrypted;
}
- catch (Exception exc) { SaLogger.Warn(SysUtil.Fn + " " + ExceptionUtil.GetErrorStateMessage(exc)); }
+ catch (Exception exc) { SaLogger.Warn($"{SysUtil.Fn} {ExceptionUtil.GetErrorStateMessage(exc)}"); }
return "";
}
public static NameValueCollection DecryptParameters(String encrypted, String base64IV, String base64Key)
@@ -253,10 +253,11 @@ namespace Sacs.Web.Components
public class ItemData_Base
{
// Common item properties.
- public String Id { get; set; }
- public String Notes { get; set; }
- public String StatusCode { get; set; }
- public String StatusDesc { get; set; }
+ public String Id { get; set; } = null;
+ private String notes;
+ public String Notes { get { return StringUtil.OnBlank(notes, null); } set { notes = StringUtil.EmptyOnNull(value).Trim(); } }
+ public String StatusCode { get; set; } = null;
+ public String StatusDesc { get; set; } = null;
// Flags for common status values.
// Return true or null so that property is not serialised when false.
public bool? Awaiting { get { return StatusCode.EqualsCI(SacsSystem.GetStatusCode(SaStatus.Awaiting)) ? true : (bool?)null; } }
diff --git a/SacsStandardBase/Base/Core/SacsDisposable.cs b/SacsStandardBase/Base/Core/SacsDisposable.cs
index 001e1db..aff00bc 100644
--- a/SacsStandardBase/Base/Core/SacsDisposable.cs
+++ b/SacsStandardBase/Base/Core/SacsDisposable.cs
@@ -6,7 +6,7 @@ namespace Sarand.Net.Standard.Base.Core
public abstract class SacsDisposable : IDisposable
{
//-------------------------------------------------------------------
- // Data memebrs.
+ // Data members.
private bool disposed = false;
//-------------------------------------------------------------------
diff --git a/SacsStandardBase/Base/Dbas/DbQuery.cs b/SacsStandardBase/Base/Dbas/DbQuery.cs
index 1b617a7..cd527e7 100644
--- a/SacsStandardBase/Base/Dbas/DbQuery.cs
+++ b/SacsStandardBase/Base/Dbas/DbQuery.cs
@@ -626,7 +626,7 @@ namespace Sarand.Net.Standard.Base.Dbas
{
if (bit > 0 && bit <= 32)
{
- // The operation depands on whether the value should be matched or not.
+ // The operation depends on whether the value should be matched or not.
String condition = (match? "<>" : "=");
// Test is achieved by shifting the value so as to drop the target bit, shift back one bit
// and identify those value that then do not match the original value shifted by one place less.
@@ -727,7 +727,7 @@ namespace Sarand.Net.Standard.Base.Dbas
{
// Must have an 'Order By' clause to use 'Offset ... Fetch Next ...'
if (orderByData.IsEmpty())
- throw new NotSupportedException("Query paging requires sort ofrder to be specified");
+ throw new NotSupportedException("Query paging requires sort order to be specified");
if (DbGlobalDbms.Type != DbmsType.MsSql)
throw new NotSupportedException("Query paging only supported for MS SQL");
query += " OFFSET " + ((paging.PageNo - 1) * paging.PageLength) + " ROWS FETCH NEXT " + paging.PageLength + " ROWS ONLY";
diff --git a/SacsStandardBase/Base/Helpers/SacsHttpClientHelper.cs b/SacsStandardBase/Base/Helpers/SacsHttpClientHelper.cs
index 9c43106..c3484c8 100644
--- a/SacsStandardBase/Base/Helpers/SacsHttpClientHelper.cs
+++ b/SacsStandardBase/Base/Helpers/SacsHttpClientHelper.cs
@@ -1,5 +1,7 @@
-using Sarand.Net.Standard.Base.App;
+using Microsoft.Net.Http.Headers;
+using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
+using Sarand.Net.Standard.Base.Net;
using Sarand.Net.Standard.Base.Tasks;
using Sarand.Net.Standard.Base.Utils;
using System;
@@ -29,7 +31,7 @@ namespace Sarand.Net.Standard.Base.Helpers
/// Request timeout. Default: 5 secs.
/// NB Not currently supported as can only be changed when static HTTP Client not in use.
///
- public int TimeoutSecs { get; set; } = RequestTimeoutSecs_Default;
+ public int TimeoutSecs { get; set; } = SacsHttpClient.RequestTimeoutSecs_Default;
///
/// Headers to send with request.
/// Accept, Accept-Charset, Accept-Encoding, Accept-Language, User-Agent, Transfer-Encoding, Pragma, Expect etc.
@@ -57,55 +59,28 @@ namespace Sarand.Net.Standard.Base.Helpers
//-------------------------------------------------------------------
/// Response data
- public class ResponseData
+ public class ResponseData : SacsHttpResponseData
{
- public DateTime RequestStarted { get; set; }
- public DateTime RequestCompleted { get; set; }
- /// Response headers.
- public Dictionary> ResponseHeaders { get; } = new Dictionary>();
- /// Response content headers.
- public Dictionary> ContentHeaders { get; } = new Dictionary>();
- /// Indicates whether Status Code represents success.
- public bool Success { get; set; }
- /// HTTP Status Code.
- public HttpStatusCode StatusCode { get; set; }
- /// HTTP message version. Default: 1.1
- public Version Version { get; set; }
- /// HTTP Reason Code.
- public String ReasonPhrase { get; set; } = "";
- /// Response data.
- public String Data { get; set; } = "";
- /// Elapsed time for request.
- public TimeSpan Elapsed { get; set; }
- /// Exception thrown during handling of request.
- public Exception Exception { get; set; }
- /// Error state message - generally exception type and message.
- public String ErrorStateMessage { get; set; }
}
//-------------------------------------------------------------------
// Data members.
- private static readonly HttpClient httpClient;
//-------------------------------------------------------------------
// Static initialisation.
- static SacsHttpClientHelper ()
+ static SacsHttpClientHelper()
{
- // Configure client.
- httpClient = new HttpClient();
- httpClient.Timeout = new TimeSpan(0, 0, RequestTimeoutSecs_Default);
}
//-------------------------------------------------------------------
// Properties.
- public static HttpClient HttpClient { get { return httpClient; } }
- public static int RequestTimeoutSecs_Default { get; } = 5;
- public static String HeaderName_ContentType { get; } = "Content-Type";
+ public static HttpClient HttpClient { get { return SacsHttpClient.HttpClient; } }
+ public static int RequestTimeoutSecs_Default { get { return SacsHttpClient.RequestTimeoutSecs_Default; } }
+ public static String HeaderName_ContentType { get {; return SacsHttpClient.HeaderName_ContentType; } }
//-------------------------------------------------------------------
- public static async Task DoRequest (RequestData requestData)
+ public static async Task DoRequest(RequestData requestData)
{
- String fn = "SacsHttpClientHelper.DoRequest";
requestData.ThrowIfCancellationRequested();
ResponseData responseData = new ResponseData();
@@ -114,14 +89,14 @@ namespace Sarand.Net.Standard.Base.Helpers
if (requestData.FormData.Count > 0)
{
postContent = new FormUrlEncodedContent(requestData.FormData);
- if (!requestData.ContentHeaders.ContainsKey(HeaderName_ContentType))
- requestData.ContentHeaders.Add(HeaderName_ContentType, "application/x-www-form-urlencoded; charset=utf-8");
+ if (!requestData.ContentHeaders.ContainsKey(SacsHttpClient.HeaderName_ContentType))
+ requestData.ContentHeaders.Add(SacsHttpClient.HeaderName_ContentType, "application/x-www-form-urlencoded; charset=utf-8");
}
else if (!StringUtil.IsEmpty(requestData.PostData) || requestData.ForcePost)
{
postContent = new StringContent(StringUtil.EmptyOnNull(requestData.PostData), Encoding.UTF8);
- if (!requestData.ContentHeaders.ContainsKey(HeaderName_ContentType))
- requestData.ContentHeaders.Add(HeaderName_ContentType, "text/plain; charset=utf-8");
+ if (!requestData.ContentHeaders.ContainsKey(SacsHttpClient.HeaderName_ContentType))
+ requestData.ContentHeaders.Add(SacsHttpClient.HeaderName_ContentType, "text/plain; charset=utf-8");
}
requestData.ThrowIfCancellationRequested();
@@ -143,7 +118,7 @@ namespace Sarand.Net.Standard.Base.Helpers
}
// Default request headers.
- if (HttpClient.DefaultRequestHeaders.Accept.Count == 0 && request.Headers.Accept.Count == 0)
+ if (SacsHttpClient.HttpClient.DefaultRequestHeaders.Accept.Count == 0 && request.Headers.Accept.Count == 0)
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/*"));
// Configure request.
@@ -163,7 +138,7 @@ namespace Sarand.Net.Standard.Base.Helpers
}
// Send request.
responseData.RequestStarted = DateTime.Now;
- response = await HttpClient.SendAsync(request, requestData.CancellationToken);
+ response = await SacsHttpClient.HttpClient.SendAsync(request, requestData.CancellationToken);
responseData.RequestCompleted = DateTime.Now;
}
else
@@ -171,7 +146,7 @@ namespace Sarand.Net.Standard.Base.Helpers
// GET.
request.Method = HttpMethod.Get;
responseData.RequestStarted = DateTime.Now;
- response = await HttpClient.SendAsync(request, requestData.CancellationToken);
+ response = await SacsHttpClient.HttpClient.SendAsync(request, requestData.CancellationToken);
responseData.RequestCompleted = DateTime.Now;
}
@@ -205,7 +180,7 @@ namespace Sarand.Net.Standard.Base.Helpers
exc = exc.InnerException;
}
String msg = exc.GetType().Name + ": " + exc.Message;
- SaLogger.Warn(fn + " " + msg + ExceptionUtil.GetStackEntries(exc, 6).Concat("", "\n\t"));
+ SaLogger.Warn(SysUtil.Fn + " " + msg + ExceptionUtil.GetStackEntries(exc, 6).Concat("", "\n\t"));
responseData.ErrorStateMessage = msg;
}
requestData.ThrowIfCancellationRequested();
@@ -218,33 +193,9 @@ namespace Sarand.Net.Standard.Base.Helpers
//-------------------------------------------------------------------
// R e s p o n s e S t a t u s D e s c r i p t i o n
//-------------------------------------------------------------------
- public static String GetResponseStatusDescription (HttpStatusCode httpStatus)
+ public static String GetResponseStatusDescription(HttpStatusCode httpStatus)
{
- // Ref. https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
- switch (httpStatus)
- {
- // 1xx: Informational - Request received, continuing process.
- case HttpStatusCode.Continue: return "Continue";
- // 2xx: Success - The action was successfully received, understood, and accepted.
- case HttpStatusCode.OK: return "OK";
- case HttpStatusCode.Created: return "Created";
- case HttpStatusCode.Accepted: return "Accepted";
- // 3xx: Redirection - Further action must be taken in order to complete the request.
- case HttpStatusCode.MultipleChoices: return "Multiple choices";
- case HttpStatusCode.MovedPermanently: return "Moved permanently";
- case HttpStatusCode.Found: return "Found";
- // 4xx: Client Error -The request contains bad syntax or cannot be fulfilled.
- case HttpStatusCode.BadRequest: return "Bad request";
- case HttpStatusCode.Unauthorized: return "Unauthorized";
- case HttpStatusCode.PaymentRequired: return "Payment required";
- case HttpStatusCode.Forbidden: return "Forbidden";
- case HttpStatusCode.NotFound: return "Not found";
- case HttpStatusCode.MethodNotAllowed: return "Method not allowed";
- case HttpStatusCode.NotAcceptable: return "Not acceptable";
- // 5xx: Server Error -The server failed to fulfill an apparently valid request.
- case HttpStatusCode.InternalServerError: return "Internal server error";
- }
- return httpStatus.ToString();
+ return SacsHttpClient.GetResponseStatusDescription(httpStatus);
}
//-------------------------------------------------------------------
diff --git a/SacsStandardBase/Base/Helpers/SacsRandomHelper.cs b/SacsStandardBase/Base/Helpers/SacsRandomHelper.cs
index 6b5bee7..96624a9 100644
--- a/SacsStandardBase/Base/Helpers/SacsRandomHelper.cs
+++ b/SacsStandardBase/Base/Helpers/SacsRandomHelper.cs
@@ -15,6 +15,7 @@ namespace Sarand.Net.Standard.Base.Helpers
private static Random random;
public static Random Random { get { if (random == null) random = new Random(GetInt32(Int32.MaxValue)); return random; } }
+ //-------------------------------------------------------------------
public static void Fill(byte[] byteArray)
{
RandomNumberGenerator.Fill(byteArray);
diff --git a/SacsStandardBase/Base/Net/SacsHttpClient.cs b/SacsStandardBase/Base/Net/SacsHttpClient.cs
index 493ca49..8e1ebc3 100644
--- a/SacsStandardBase/Base/Net/SacsHttpClient.cs
+++ b/SacsStandardBase/Base/Net/SacsHttpClient.cs
@@ -1,23 +1,31 @@
-using Sarand.Net.Standard.Base.App;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Net.Http.Headers;
+using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
+using Sarand.Net.Standard.Base.Helpers;
using Sarand.Net.Standard.Base.Tasks;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
+using System.IO.Compression;
+using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
+using System.Reflection.Metadata;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using ZstdNet;
namespace Sarand.Net.Standard.Base.Net
{
//=======================================================================
public class SacsHttpRequestData : SacsTaskData
{
- public SacsHttpRequestData (CancellationToken? cancellationToken) : base(cancellationToken) { }
+ public SacsHttpRequestData(CancellationToken? cancellationToken) : base(cancellationToken) { }
/// URL to send request to.
public String Url { get; set; }
///
@@ -50,6 +58,21 @@ namespace Sarand.Net.Standard.Base.Net
public bool ForcePost { get; set; }
}
+ //=======================================================================
+ public class SacsHttpCookieData
+ {
+ public String HeaderValue { get; set; }
+ public String Name { get; set; }
+ public String Value { get; set; }
+ public DateTime Expires { get; set; }
+ public long MaxAge { get; set; }
+ public String Path { get; set; }
+ public String Domain { get; set; }
+ public bool Secure { get; set; }
+ public bool HttpOnly { get; set; }
+ public Dictionary Properties { get; } = new Dictionary();
+ }
+
//=======================================================================
public class SacsHttpResponseData
{
@@ -59,10 +82,14 @@ namespace Sarand.Net.Standard.Base.Net
public Dictionary> ResponseHeaders { get; } = new Dictionary>();
/// Response content headers.
public Dictionary> ContentHeaders { get; } = new Dictionary>();
+ /// Response cookies from 'Set-Cookie' header.
+ public Dictionary Cookies { get; } = new Dictionary();
/// Indicates whether Status Code represents success.
public bool Success { get; set; }
/// HTTP Status Code.
public HttpStatusCode StatusCode { get; set; }
+ /// HTTP message version. Default: 1.1
+ public Version Version { get; set; }
/// HTTP Reason Code.
public String ReasonPhrase { get; set; } = "";
/// Response data.
@@ -80,26 +107,28 @@ namespace Sarand.Net.Standard.Base.Net
{
//-------------------------------------------------------------------
// Data members.
- private static readonly HttpClient client;
+ private static readonly HttpClient httpClient;
//-------------------------------------------------------------------
// Static initialisation.
- static SacsHttpClient ()
+ static SacsHttpClient()
{
// Configure client.
- client = new HttpClient();
- client.Timeout = new TimeSpan(0, 0, RequestTimeoutSecs_Default);
+ httpClient = new HttpClient();
+ httpClient.Timeout = new TimeSpan(0, 0, RequestTimeoutSecs_Default);
+ ProductInfoHeaderValue productInfoHeaderValue = new ProductInfoHeaderValue("Saratoga", "1.2.3");
+ httpClient.DefaultRequestHeaders.UserAgent.Add(productInfoHeaderValue);
}
//-------------------------------------------------------------------
// Properties.
+ public static HttpClient HttpClient { get { return httpClient; } }
public static int RequestTimeoutSecs_Default { get; } = 5;
- public static String HeaderName_ContentType { get; } = "Content-Type";
+ public static String HeaderName_ContentType { get; } = HeaderNames.ContentType;
//-------------------------------------------------------------------
- public static async Task DoRequest (SacsHttpRequestData requestData)
+ public static async Task DoRequest(SacsHttpRequestData requestData)
{
- String fn = "SacsHttpClient.DoRequest";
SacsHttpResponseData responseData = new SacsHttpResponseData();
requestData.ThrowIfCancellationRequested();
@@ -109,13 +138,13 @@ namespace Sarand.Net.Standard.Base.Net
{
postContent = new FormUrlEncodedContent(requestData.FormData);
if (!requestData.ContentHeaders.ContainsKey(HeaderName_ContentType))
- requestData.ContentHeaders.Add(HeaderName_ContentType,"application/x-www-form-urlencoded; charset=utf-8");
+ requestData.ContentHeaders.Add(HeaderName_ContentType, "application/x-www-form-urlencoded; charset=utf-8");
}
else if (!StringUtil.IsEmpty(requestData.PostData) || requestData.ForcePost)
{
postContent = new StringContent(StringUtil.EmptyOnNull(requestData.PostData), Encoding.UTF8);
if (!requestData.ContentHeaders.ContainsKey(HeaderName_ContentType))
- requestData.ContentHeaders.Add(HeaderName_ContentType,"text/plain; charset=utf-8");
+ requestData.ContentHeaders.Add(HeaderName_ContentType, "text/plain; charset=utf-8");
}
requestData.ThrowIfCancellationRequested();
@@ -137,7 +166,7 @@ namespace Sarand.Net.Standard.Base.Net
}
// Default request headers.
- if (client.DefaultRequestHeaders.Accept.Count == 0 && request.Headers.Accept.Count == 0)
+ if (httpClient.DefaultRequestHeaders.Accept.Count == 0 && request.Headers.Accept.Count == 0)
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/*"));
// Configure request.
@@ -157,7 +186,7 @@ namespace Sarand.Net.Standard.Base.Net
}
// Send request.
responseData.RequestStarted = DateTime.Now;
- response = await client.SendAsync(request, requestData.CancellationToken);
+ response = await httpClient.SendAsync(request, requestData.CancellationToken);
responseData.RequestCompleted = DateTime.Now;
}
else
@@ -165,25 +194,61 @@ namespace Sarand.Net.Standard.Base.Net
// GET.
request.Method = HttpMethod.Get;
responseData.RequestStarted = DateTime.Now;
- response = await client.SendAsync(request, requestData.CancellationToken);
+ response = await httpClient.SendAsync(request, requestData.CancellationToken);
responseData.RequestCompleted = DateTime.Now;
}
- // Extract resopnse data.
+ // Extract response data.
+ responseData.Version = response.Version;
responseData.Success = response.IsSuccessStatusCode;
responseData.StatusCode = response.StatusCode;
responseData.ReasonPhrase = response.ReasonPhrase;
+ // Extract response headers.
if (response.Headers != null)
{
foreach (var header in response.Headers)
responseData.ResponseHeaders[header.Key] = new List(header.Value);
}
+ // Extract response content headers.
if (response.Content != null && response.Content.Headers != null)
{
foreach (var header in response.Content.Headers)
responseData.ContentHeaders[header.Key] = new List(header.Value);
}
- responseData.Data = await response.Content.ReadAsStringAsync();
+ // Extract response cookies.
+ if (response.Headers.Contains(HeaderNames.SetCookie))
+ {
+ foreach (var cookieHeader in response.Headers.GetValues(HeaderNames.SetCookie))
+ {
+ SacsHttpCookieData cookie = ExtractCookieFromHeader(cookieHeader);
+ if (cookie != null && !StringUtil.IsBlank(cookie.Name))
+ responseData.Cookies.Add(cookie.Name, cookie);
+ }
+ }
+ // Extract response payload.
+ var contentEncodings = responseData.ContentHeaders.GetValueOrDefault(HeaderNames.ContentEncoding, new List());
+ using (var responseStream = await response.Content.ReadAsStreamAsync())
+ {
+ // Check if response needs to be decompressed, as directed by 'content-encoding' header.
+ Stream decompressedStream = null;
+ if (contentEncodings.ContainsCI("gzip"))
+ decompressedStream = new GZipStream(responseStream, CompressionMode.Decompress);
+ else if (contentEncodings.ContainsCI("deflate"))
+ decompressedStream = new DeflateStream(responseStream, CompressionMode.Decompress);
+ else if (contentEncodings.ContainsCI("br"))
+ decompressedStream = new BrotliStream(responseStream, CompressionMode.Decompress);
+ else if (contentEncodings.ContainsCI("zstd"))
+ decompressedStream = new DecompressionStream(responseStream);
+
+ // Read the response payload, decompressed if necessary.
+ using (var streamReader = new StreamReader(decompressedStream != null ? decompressedStream : responseStream))
+ responseData.Data = await streamReader.ReadToEndAsync();
+
+ // Clean-up.
+ if (decompressedStream != null)
+ decompressedStream.Dispose();
+ }
+ // Clean-up.
if (response != null) response.Dispose();
}
}
@@ -198,7 +263,7 @@ namespace Sarand.Net.Standard.Base.Net
exc = exc.InnerException;
}
String msg = exc.GetType().Name + ": " + exc.Message;
- SaLogger.Warn(fn + " " + msg + ExceptionUtil.GetStackEntries(exc, 6).Concat("", "\n\t"));
+ SaLogger.Warn(SysUtil.Fn + " " + msg + ExceptionUtil.GetStackEntries(exc, 6).Concat("", "\n\t"));
responseData.ErrorStateMessage = msg;
}
requestData.ThrowIfCancellationRequested();
@@ -208,10 +273,57 @@ namespace Sarand.Net.Standard.Base.Net
return responseData;
}
+ //-------------------------------------------------------------------
+ // Cookies.
+ //-------------------------------------------------------------------
+ public static SacsHttpCookieData ExtractCookieFromHeader(String cookieHeader)
+ {
+ SacsHttpCookieData cookie = null;
+ String[] cookieParts = cookieHeader.Split(';');
+ if (cookieParts.Count() > 1)
+ {
+ cookie = new SacsHttpCookieData();
+ cookie.HeaderValue = cookieHeader;
+ // Name and value.
+ String[] cookieNameValue = cookieParts[0].Trim().Split(new char[] { '=' }, 2);
+ cookie.Name = cookieNameValue[0].Trim();
+ cookie.Value = cookieNameValue.Count() > 1 ? cookieNameValue[1].Trim() : "";
+ // Other properties.
+ for (int i = 1; i < cookieParts.Length; i++)
+ {
+ string[] properties = cookieParts[i].Trim().Split(new char[] { '=' }, 2);
+ String propertyName = properties[0].Trim();
+ String propertyValue = properties.Count() > 1 ? properties[1].Trim() : "True";
+ cookie.Properties.Add(propertyName, propertyValue);
+ if (propertyName.EqualsCI("Domain"))
+ cookie.Domain = propertyValue;
+ else if (propertyName.EqualsCI("Path"))
+ cookie.Path = propertyValue;
+ else if (propertyName.EqualsCI("Expires"))
+ {
+ cookie.Expires = DateUtil.ParseExact(propertyValue, "ddd, d MMM yyyy HH:mm:ss GMT");
+ if (!DateUtil.IsValid(cookie.Expires))
+ {
+ DateTime expires;
+ DateTime.TryParse(propertyValue, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out expires);
+ cookie.Expires = expires;
+ }
+ }
+ else if (propertyName.EqualsCI("Max-Age"))
+ cookie.MaxAge = StringUtil.ToLong(propertyValue);
+ else if (propertyName.EqualsCI("Secure"))
+ cookie.Secure = StringUtil.ToBool(propertyValue);
+ else if (propertyName.EqualsCI("HttpOnly"))
+ cookie.HttpOnly = StringUtil.ToBool(propertyValue);
+ }
+ }
+ return cookie;
+ }
+
//-------------------------------------------------------------------
// R e s p o n s e S t a t u s D e s c r i p t i o n
//-------------------------------------------------------------------
- public static String GetResponseStatusDescription (HttpStatusCode httpStatus)
+ public static String GetResponseStatusDescription(HttpStatusCode httpStatus)
{
// Ref. https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
switch (httpStatus)
diff --git a/SacsStandardBase/Base/Net/WebUtil.cs b/SacsStandardBase/Base/Net/WebUtil.cs
index 2e42283..8311b6e 100644
--- a/SacsStandardBase/Base/Net/WebUtil.cs
+++ b/SacsStandardBase/Base/Net/WebUtil.cs
@@ -1,4 +1,5 @@
-using Sarand.Net.Standard.Base.Core;
+using Org.BouncyCastle.Asn1.Ocsp;
+using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.Utils;
using System;
using System.Collections.Generic;
@@ -13,13 +14,13 @@ namespace Sarand.Net.Standard.Base.Net
// H T M L E n c o d i n g
//-------------------------------------------------------------------
/// Converts a string to an HTML-encoded string.
- public static String HtmlEncode (this String value)
+ public static String HtmlEncode(this String value)
{
if (StringUtil.IsEmpty(value)) return value;
return WebUtility.HtmlEncode(value);
}
/// Converts a string that has been HTML-encoded for HTTP transmission into a decoded string.
- public static String HtmlDecode (this String value)
+ public static String HtmlDecode(this String value)
{
if (StringUtil.IsEmpty(value)) return value;
return WebUtility.HtmlDecode(value);
@@ -29,18 +30,64 @@ namespace Sarand.Net.Standard.Base.Net
// U R L E n c o d i n g
//-------------------------------------------------------------------
/// Converts a string to an HTML-encoded string.
- public static String UrlEncode (this String value)
+ public static String UrlEncode(this String value)
{
if (StringUtil.IsEmpty(value)) return value;
return WebUtility.UrlEncode(value);
}
/// Converts a string that has been HTML-encoded for HTTP transmission into a decoded string.
- public static String UrlDecode (this String value)
+ public static String UrlDecode(this String value)
{
if (StringUtil.IsEmpty(value)) return value;
return WebUtility.UrlDecode(value);
}
+ //-------------------------------------------------------------------
+ // URI Builder.
+
+ ///
+ /// Configure HTTP URI builder.
+ ///
+ public static UriBuilder ConfigureUriBuilder(this UriBuilder uriBuilder, String host, String path, String query = null)
+ {
+ uriBuilder.Scheme = "http";
+ uriBuilder.Host = StringUtil.OnBlank(host);
+ uriBuilder.Path = StringUtil.OnBlank(path);
+ uriBuilder.Query = StringUtil.OnBlank(query);
+ return uriBuilder;
+ }
+
+ ///
+ /// Create HTTP URI builder.
+ ///
+ public static UriBuilder CreateUriBuilder(String host, String path, String query = null)
+ {
+ UriBuilder uriBuilder = new UriBuilder();
+ return uriBuilder.ConfigureUriBuilder(host, path, query);
+ }
+
+ ///
+ /// Configure HTTPS URI builder.
+ ///
+ public static UriBuilder ConfigureSslUriBuilder(this UriBuilder uriBuilder, String host, String path, String query = null)
+ {
+ uriBuilder.Scheme = "https";
+ uriBuilder.Port = 443;
+ uriBuilder.Host = StringUtil.OnBlank(host);
+ uriBuilder.Path = StringUtil.OnBlank(path);
+ uriBuilder.Query = StringUtil.OnBlank(query);
+ return uriBuilder;
+ }
+
+ ///
+ /// Create HTTPS URI builder.
+ ///
+ public static UriBuilder CreateSslUriBuilder(String host, String path, String query = null)
+ {
+ UriBuilder uriBuilder = new UriBuilder();
+ return uriBuilder.ConfigureSslUriBuilder(host, path, query);
+ }
+
//-------------------------------------------------------------------
}
}
diff --git a/SacsStandardBase/Base/Tasks/SacsTaskData.cs b/SacsStandardBase/Base/Tasks/SacsTaskData.cs
index a7bdfe5..386ab51 100644
--- a/SacsStandardBase/Base/Tasks/SacsTaskData.cs
+++ b/SacsStandardBase/Base/Tasks/SacsTaskData.cs
@@ -12,7 +12,7 @@ namespace Sarand.Net.Standard.Base.Tasks
//-------------------------------------------------------------------
// Data members.
// Cancellation token source - allows task cancellation to be triggered
- // if the cancellation tokden wasn't supplied on construction.
+ // if the cancellation token wasn't supplied on construction.
private CancellationTokenSource cancellationTokenSource = null;
//-------------------------------------------------------------------
diff --git a/SacsStandardBase/Base/Utils/ExceptionUtil.cs b/SacsStandardBase/Base/Utils/ExceptionUtil.cs
index 2248291..d5c6dc6 100644
--- a/SacsStandardBase/Base/Utils/ExceptionUtil.cs
+++ b/SacsStandardBase/Base/Utils/ExceptionUtil.cs
@@ -59,9 +59,12 @@ namespace Sarand.Net.Standard.Base.Utils
public static List GetStackEntries(this Exception exc, int entries = 0)
{
List stackEntries = new List();
- stackEntries.AddRange(exc.StackTrace.Split(new String[] { "\r\n" }, (entries > 0 ? entries : 999), StringSplitOptions.None));
- for (int idx = 0; idx < stackEntries.Count; idx++)
- stackEntries[idx] = stackEntries[idx].Replace(" at ", "");
+ if (exc.StackTrace != null)
+ {
+ stackEntries.AddRange(exc.StackTrace.Split(new String[] { "\r\n" }, (entries > 0 ? entries : 999), StringSplitOptions.None));
+ for (int idx = 0; idx < stackEntries.Count; idx++)
+ stackEntries[idx] = stackEntries[idx].Replace(" at ", "");
+ }
return stackEntries;
}
diff --git a/SacsStandardBase/Base/Utils/ListUtil.cs b/SacsStandardBase/Base/Utils/ListUtil.cs
index ebf3538..415644a 100644
--- a/SacsStandardBase/Base/Utils/ListUtil.cs
+++ b/SacsStandardBase/Base/Utils/ListUtil.cs
@@ -9,7 +9,7 @@ namespace Sarand.Net.Standard.Base.Utils
public static class ListUtil
{
//-------------------------------------------------------------------
- public static void ClearAndDispose (this IList values) where T : IDisposable
+ public static void ClearAndDispose(this IList values) where T : IDisposable
{
while (values.Count > 0)
{
@@ -50,7 +50,12 @@ namespace Sarand.Net.Standard.Base.Utils
/// Unique values only - ignores number of instances of each value.
UniqueValuesOnly = 0x02,
}
- public static bool Equals (this IList sourceList, IList targetList, Equality equality)
+ ///
+ /// Compare content of two lists.
+ /// - Exact ensures precise number and sequence of elements.
+ /// - Unique allows any number and sequence as long as each item is present in both lists.
+ ///
+ public static bool Equals(this IList sourceList, IList targetList, Equality equality)
{
bool sameLength = (sourceList.Count == targetList.Count);
if (equality == Equality.Exact)
@@ -69,6 +74,22 @@ namespace Sarand.Net.Standard.Base.Utils
return true;
}
+ //-------------------------------------------------------------------
+ ///
+ /// Remove the specified range of items from the list to a new list.
+ ///
+ public static List ExtractRange(this List values, int index, int count)
+ {
+ List extractedValues = new List();
+ if (values.Any())
+ {
+ count = Math.Min(values.Count(), count);
+ extractedValues.AddRange(values.GetRange(index, count));
+ values.RemoveRange(index, count);
+ }
+ return extractedValues;
+ }
+
//-------------------------------------------------------------------
}
//=======================================================================
diff --git a/SacsStandardBase/Base/Utils/NewtonsoftJsonUtil.cs b/SacsStandardBase/Base/Utils/NewtonsoftJsonUtil.cs
new file mode 100644
index 0000000..11ba571
--- /dev/null
+++ b/SacsStandardBase/Base/Utils/NewtonsoftJsonUtil.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json.Linq;
+
+namespace Sarand.Net.Standard.Base.Utils
+{
+ //=======================================================================
+ public static class NewtonsoftJsonUtil
+ {
+ //-------------------------------------------------------------------
+ // Extracting.
+ //-------------------------------------------------------------------
+
+ //-------------------------------------------------------------------
+ // Parsing.
+ //-------------------------------------------------------------------
+
+ /// Parse data as JSON Token.
+ public static JToken ParseJsonToken(string jsonData)
+ {
+ return JToken.Parse(jsonData);
+ }
+
+ /// Parse data as JSON Token.
+ public static JToken ParseJsonToken(string jsonData, out Exception excOut)
+ {
+ excOut = null;
+ try
+ {
+ return JToken.Parse(jsonData);
+ }
+ catch (Exception exc)
+ {
+ excOut = exc;
+ }
+ return null;
+ }
+
+ //-------------------------------------------------------------------
+
+ /// Parse data as JSON Object.
+ public static JObject ParseJsonObject(string jsonData)
+ {
+ return JObject.Parse(jsonData);
+ }
+
+ /// Parse data as JSON Object.
+ public static JObject ParseJsonObject(string jsonData, out Exception excOut)
+ {
+ excOut = null;
+ try
+ {
+ return JObject.Parse(jsonData);
+ }
+ catch (Exception exc)
+ {
+ excOut = exc;
+ }
+ return null;
+ }
+
+ //-------------------------------------------------------------------
+ /// Parse data as JSON Object.
+ public static JArray ParseJsonArray(string jsonData)
+ {
+ return JArray.Parse(jsonData);
+ }
+
+ /// Parse data as JSON Object.
+ public static JArray ParseJsonArray(string jsonData, out Exception excOut)
+ {
+ excOut = null;
+ try
+ {
+ return JArray.Parse(jsonData);
+ }
+ catch (Exception exc)
+ {
+ excOut = exc;
+ }
+ return null;
+ }
+
+ //-------------------------------------------------------------------
+ }
+ //=======================================================================
+}
diff --git a/SacsStandardBase/Base/Utils/StringUtil.cs b/SacsStandardBase/Base/Utils/StringUtil.cs
index ba15816..7bf92cc 100644
--- a/SacsStandardBase/Base/Utils/StringUtil.cs
+++ b/SacsStandardBase/Base/Utils/StringUtil.cs
@@ -66,7 +66,7 @@ namespace Sarand.Net.Standard.Base.Utils
// Regular expressions.
//-------------------------------------------------------------------
///
- /// Create Regex from (quoted) expression and apply Regex.Match to String value and return Match.Success.
+ /// Apply Regex.Match to String value and return Match.Success.
/// - Case sensitive.
///
public static bool Match(this String value, String expression)
@@ -74,7 +74,7 @@ namespace Sarand.Net.Standard.Base.Utils
return value.Match(expression, RegexOptions.None);
}
///
- /// Create Regex from (quoted) expression and apply Regex.Match to String value and return Match.Success.
+ /// Apply Regex.Match to String value and return Match.Success, ignoring case.
/// - Case insensitive.
///
public static bool MatchCI(this String value, String expression)
@@ -82,12 +82,11 @@ namespace Sarand.Net.Standard.Base.Utils
return value.Match(expression, RegexOptions.IgnoreCase);
}
///
- /// Create Regex from (quoted) expression and apply Regex.Match to String value and return Match.Success.
- /// - Supply options as required.
+ /// Apply Regex.Match to String value and return Match.Success.
///
public static bool Match(this String value, String expression, RegexOptions options)
{
- return new Regex(Regex.Escape(expression), options).Match(value).Success;
+ return Regex.Match(value, expression, options).Success;
}
///
/// Apply Regex.Match to String value and return Match.Success.
@@ -96,12 +95,67 @@ namespace Sarand.Net.Standard.Base.Utils
{
return re.Match(value).Success;
}
+
///
- /// Apply Regex.Matches to String value and return true if any matches found.
+ /// Apply Regex.IsMatch to String value.
+ /// - Case sensitive.
///
- public static bool Matches(this String value, Regex re)
+ public static bool IsMatch(this String value, String expression)
{
- return re.Matches(value).Count < 1;
+ return value.IsMatch(expression, RegexOptions.None);
+ }
+ ///
+ /// Apply Regex.IsMatch to String value, ignoring case.
+ /// - Case insensitive.
+ ///
+ public static bool IsMatchCI(this String value, String expression)
+ {
+ return value.IsMatch(new Regex(Regex.Escape(expression), RegexOptions.IgnoreCase));
+ }
+ ///
+ /// Apply Regex.IsMatch to String value.
+ ///
+ public static bool IsMatch(this String value, String expression, RegexOptions options)
+ {
+ return Regex.IsMatch(value, expression, options);
+ }
+ ///
+ /// Apply Regex.IsMatch to String value.
+ ///
+ public static bool IsMatch(this String value, Regex re)
+ {
+ return re.IsMatch(value);
+ }
+
+ ///
+ /// Apply Regex.Matches to String value and return number of matches found.
+ /// - Case sensitive.
+ ///
+ public static int MatchCount(this String value, String expression)
+ {
+ return value.MatchCount(expression, RegexOptions.None);
+ }
+ ///
+ /// Apply Regex.Matches to String value and return number of matches found.
+ /// - Case insensitive.
+ ///
+ public static int MatchCountCI(this String value, String expression)
+ {
+ return value.MatchCount(expression, RegexOptions.IgnoreCase);
+ }
+ ///
+ /// Apply Regex.Matches to String value and return number of matches found.
+ ///
+ public static int MatchCount(this String value, String expression, RegexOptions options)
+ {
+ return Regex.Matches(value, expression, options).Count;
+ }
+ ///
+ /// Apply Regex.Matches to String value and return number of matches found.
+ ///
+ public static int MatchCount(this String value, Regex re)
+ {
+ return re.Matches(value).Count;
}
//-------------------------------------------------------------------
diff --git a/SacsStandardBase/Base/Utils/WebUtil.cs b/SacsStandardBase/Base/Utils/WebUtil.cs
deleted file mode 100644
index 889c969..0000000
--- a/SacsStandardBase/Base/Utils/WebUtil.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using Sarand.Net.Standard.Base.Core;
-using System;
-using System.Collections.Generic;
-using System.Net;
-using System.Text;
-
-namespace Sarand.Net.Standard.Base.Utils
-{
- public static class WebUtil
- {
- //-------------------------------------------------------------------
- // H T M L E n c o d i n g
- //-------------------------------------------------------------------
- /// Converts a string to an HTML-encoded string.
- public static String HtmlEncode (this String value)
- {
- if (StringUtil.IsEmpty(value)) return value;
- return WebUtility.HtmlEncode(value);
- }
- /// Converts a string that has been HTML-encoded for HTTP transmission into a decoded string.
- public static String HtmlDecode (this String value)
- {
- if (StringUtil.IsEmpty(value)) return value;
- return WebUtility.HtmlDecode(value);
- }
-
- //-------------------------------------------------------------------
- // U R L E n c o d i n g
- //-------------------------------------------------------------------
- /// Converts a string to an HTML-encoded string.
- public static String UrlEncode (this String value)
- {
- if (StringUtil.IsEmpty(value)) return value;
- return WebUtility.UrlEncode(value);
- }
- /// Converts a string that has been HTML-encoded for HTTP transmission into a decoded string.
- public static String UrlDecode (this String value)
- {
- if (StringUtil.IsEmpty(value)) return value;
- return WebUtility.UrlDecode(value);
- }
-
- //-------------------------------------------------------------------
- }
-}
diff --git a/SacsStandardBase/SacsStandardBase.csproj b/SacsStandardBase/SacsStandardBase.csproj
index 1b41118..cd7772e 100644
--- a/SacsStandardBase/SacsStandardBase.csproj
+++ b/SacsStandardBase/SacsStandardBase.csproj
@@ -7,11 +7,12 @@
-
+
+
diff --git a/SacsStandardDbas/Dbas/Base/DatabaseDefinitionGenerator.cs b/SacsStandardDbas/Dbas/Base/DatabaseDefinitionGenerator.cs
index 86e559d..65562fe 100644
--- a/SacsStandardDbas/Dbas/Base/DatabaseDefinitionGenerator.cs
+++ b/SacsStandardDbas/Dbas/Base/DatabaseDefinitionGenerator.cs
@@ -20,7 +20,7 @@ namespace Sarand.Net.Standard.Dbas.Base
{
VS2017,
///
- /// Visual Studio 2019 version. StringListUtil rather than StringListUtil.
+ /// Visual Studio 2019 version. StringListUtil rather than StringList.
///
VS2019
}
@@ -36,7 +36,7 @@ namespace Sarand.Net.Standard.Dbas.Base
}
public class Config
{
- public SarandCommonVersion SarandCommonVersion { get; set; } = SarandCommonVersion.VS2017;
+ public SarandCommonVersion SarandCommonVersion { get; set; } = SarandCommonVersion.VS2019;
public DotNetVersion DotNetVersion { get; set; } = DotNetVersion.Framework;
public SacsDatabase Database { get; set; }
public Dbms DbmsOutput { get; set; } = Dbms.ODBC;
@@ -46,10 +46,12 @@ namespace Sarand.Net.Standard.Dbas.Base
public class SchemaData
{
// Tables to be included (all others excluded) or those to be excluded.
- public List Tables_Included { get; } = new List();
- public List Tables_Excluded { get; } = new List();
+ public HashSet Tables_Included { get; } = new HashSet(StringComparer.InvariantCultureIgnoreCase);
+ public HashSet Tables_Excluded { get; } = new HashSet(StringComparer.InvariantCultureIgnoreCase);
+ // Schema capitalisation - for class name prefix etc - where capitalising first letter is insufficient.
+ public Dictionary SchemaNameCapitalisation { get; } = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
// Alternative table names.
- public Dictionary> AlternativeTableNames { get; } = new Dictionary>();
+ public Dictionary> AlternativeTableNames { get; } = new Dictionary>(StringComparer.InvariantCultureIgnoreCase);
public void AddAlternativeTableName(String tableName, String tableNameAlternative)
{
if (!AlternativeTableNames.ContainsKey(tableName)) AlternativeTableNames[tableName] = new List();
@@ -66,7 +68,7 @@ namespace Sarand.Net.Standard.Dbas.Base
public class DefinitionClassConfig
{
/// Using namespaces for database definition and table class usings.
- public List Usings { get; } = new List() { "Sarand.Net.Standard.Base.Core", "Sarand.Net.Standard.Base.Dbas" };
+ public HashSet Usings { get; } = new HashSet() { "Sarand.Net.Standard.Base.Core", "Sarand.Net.Standard.Base.Dbas" };
public String Namespace { get; set; } = "Sarand.Application.Core.Base";
public String Classname { get; set; } = "SaDbDfn";
}
@@ -75,7 +77,7 @@ namespace Sarand.Net.Standard.Dbas.Base
public class TableClassConfig
{
/// Using namespaces for table classes.
- public List Usings { get; } = new List() { "Sarand.Net.Standard.Base.Core", "Sarand.Net.Standard.Base.Dbas" };
+ public HashSet Usings { get; } = new HashSet() { "Sarand.Net.Standard.Base.Core", "Sarand.Net.Standard.Base.Dbas" };
public String Namespace_BaseClass { get; set; } = "Sarand.Net.Standard.Dbas.Data";
public String Namespace { get; set; } = "Sarand.Application.Dbas";
public String BaseClass { get; set; } = "DbDataItem";
@@ -89,6 +91,7 @@ namespace Sarand.Net.Standard.Dbas.Base
//-------------------------------------------------------------------
// Data members.
private Config config = null;
+ private const String DefaultSchema_MSSQL = "dbo";
private static String /*lf = "\n",*/ crlf = "\r\n", tab1 = "\t", tab2 = "\t\t", tab3 = "\t\t\t", tab4 = "\t\t\t\t", tab5 = "\t\t\t\t\t";
// Reserved words.
private static readonly String[] reservedWords =
@@ -313,10 +316,19 @@ namespace Sarand.Net.Standard.Dbas.Base
file.WriteLine(tab2 + "{");
file.WriteLine(tab3 + "int tableIdx = (int)table - 1;");
file.WriteLine(tab3 + "if (tableIdx < 0 || tableIdx >= tableCount) return String.Empty;");
- file.WriteLine(tab3 + "String tableName = tables[(int)tableIdx];");
+ file.WriteLine(tab3 + "String tableName = tables[(int)tableIdx].Name;");
file.WriteLine(tab3 + "if (delimitReservedWords) tableName = DelimitReservedWord(tableName);");
file.WriteLine(tab3 + "return tableName;");
file.WriteLine(tab2 + "}");
+ file.WriteLine(tab2 + "public static String GetTableNameQual (TableIdx table, bool delimitReservedWords = true)");
+ file.WriteLine(tab2 + "{");
+ file.WriteLine(tab3 + "int tableIdx = (int)table - 1;");
+ file.WriteLine(tab3 + "if (tableIdx < 0 || tableIdx >= tableCount) return String.Empty;");
+ file.WriteLine(tab3 + "String schemaName = tables[(int)tableIdx].Schema;");
+ file.WriteLine(tab3 + "String tableName = tables[(int)tableIdx].Name;");
+ file.WriteLine(tab3 + "if (delimitReservedWords) { schemaName = DelimitReservedWord(schemaName); tableName = DelimitReservedWord(tableName); }");
+ file.WriteLine(tab3 + "return (StringUtil.IsBlank(schemaName) ? \"\" : schemaName + \".\") + tableName;");
+ file.WriteLine(tab2 + "}");
file.WriteLine(crlf + tab2 + "//-------------------------------------------------------------------");
// Table for Column method.
@@ -369,8 +381,15 @@ namespace Sarand.Net.Standard.Dbas.Base
file.WriteLine(tab2 + "}");
file.WriteLine(tab2 + "public static String DelimitReservedWord (String word)");
file.WriteLine(tab2 + "{");
- file.WriteLine(tab3 + "if (!IsReservedWord(word)) return word;");
- file.WriteLine(tab3 + "switch (reservedWordDelim)");
+ file.WriteLine(tab3 + "return IsReservedWord(word) ? DelimitWord(word) : word;");
+ file.WriteLine(tab2 + "}");
+ file.WriteLine(tab2 + "public static String DelimitWord (String word)");
+ file.WriteLine(tab2 + "{");
+ file.WriteLine(tab3 + "return DelimitWord(word, reservedWordDelim);");
+ file.WriteLine(tab2 + "}");
+ file.WriteLine(tab2 + "public static String DelimitWord (String word, DbReservedWordDelim wordDelim)");
+ file.WriteLine(tab2 + "{");
+ file.WriteLine(tab3 + "switch (wordDelim)");
file.WriteLine(tab3 + "{");
file.WriteLine(tab4 + "case DbReservedWordDelim.None:");
file.WriteLine(tab5 + "break;");
@@ -447,12 +466,22 @@ namespace Sarand.Net.Standard.Dbas.Base
file.WriteLine(tab2 + "}");
file.WriteLine(crlf + tab2 + "//-------------------------------------------------------------------");
- // Table definitions.
+ // Table names.
file.WriteLine(tab2 + "// Table definitions.");
- file.WriteLine(tab2 + "private static readonly String[] tables =");
+ file.WriteLine(tab2 + "private class TableDfn");
+ file.WriteLine(tab2 + "{");
+ file.WriteLine(tab3 + "internal String Schema { get; set; }");
+ file.WriteLine(tab3 + "internal String Name { get; set; }");
+ file.WriteLine(tab3 + "internal TableDfn (String schema, String name)");
+ file.WriteLine(tab3 + "{");
+ file.WriteLine(tab4 + "this.Schema= schema;");
+ file.WriteLine(tab4 + "this.Name = name;");
+ file.WriteLine(tab3 + "}");
+ file.WriteLine(tab2 + "}");
+ file.WriteLine(tab2 + "private static readonly TableDfn[] tables =");
file.WriteLine(tab2 + "{");
foreach (DbDfnTable tableDfn in tableDfns)
- file.WriteLine(tab3 + "\"" + tableDfn.Name + "\", ");
+ file.WriteLine(tab3 + "new TableDfn(\"" + tableDfn.Schema + "\",\"" + tableDfn.Name + "\"),");
file.WriteLine(tab2 + "};");
file.WriteLine(crlf + tab2 + "//-------------------------------------------------------------------");
@@ -476,7 +505,6 @@ namespace Sarand.Net.Standard.Dbas.Base
file.WriteLine(tab4 + "this.DataLen = dataLen;");
file.WriteLine(tab3 + "}");
file.WriteLine(tab2 + "}");
- file.WriteLine(crlf + tab2 + "//-------------------------------------------------------------------");
file.WriteLine(tab2 + "private static readonly ColumnDfn[] columns =");
file.WriteLine(tab2 + "{");
uint tableIdx = 0;
@@ -505,6 +533,10 @@ namespace Sarand.Net.Standard.Dbas.Base
private void GenerateTableInterfaceClass(DbDfnTable tableDfn)
{
String className = tableDfn.Name + "_Db";
+ if (!StringUtil.IsEmpty(tableDfn.Schema) && !tableDfn.Schema.EqualsCI("dbo"))
+ {
+ className = config.Schema.SchemaNameCapitalisation.GetValueOrDefault(tableDfn.Schema, StringUtil.CapitaliseFirst(tableDfn.Schema)) + "_" + className;
+ }
List primaryColumnNames = tableDfn.GetPrimaryColumnNames();
String identityColumnName = tableDfn.Columns.GetIdentityColumnName();
String identityColumnVariableTypeName = tableDfn.Columns.GetIdentityColumnVariableTypeName();
diff --git a/SacsStandardDbas/Dbas/Base/SacsDatabaseConsole.cs b/SacsStandardDbas/Dbas/Base/SacsDatabaseConsole.cs
index 77c0402..f4e43ed 100644
--- a/SacsStandardDbas/Dbas/Base/SacsDatabaseConsole.cs
+++ b/SacsStandardDbas/Dbas/Base/SacsDatabaseConsole.cs
@@ -141,7 +141,7 @@ namespace Sarand.Net.Standard.Dbas.Base
if (StringUtil.IsEmpty(config.DatabaseName))
config.DatabaseName = sqlDatabase;
- SaLogger.Info("DatabaseConsole.GenerateConnectionString Driver: " + sqlDriver + ". Server: " + sqlServer + ". Database: " + sqlDatabase + ". Connection: " + connectionString);
+ SaLogger.Info($"{SysUtil.Fn} Driver: " + sqlDriver + ". Server: " + sqlServer + ". Database: " + sqlDatabase + ". Connection: " + connectionString);
}
return connectionString;
}
@@ -219,7 +219,7 @@ namespace Sarand.Net.Standard.Dbas.Base
else
{
#if DEBUG
- SaLogger.Debug("DropDbIndex Table: " + tableName + "; Index: " + indexName + "; Exception: " + database.ErrorStateMessage);
+ SaLogger.Debug($"{SysUtil.Fn} Table: " + tableName + "; Index: " + indexName + "; Exception: " + database.ErrorStateMessage);
#endif
SetErrorState("Failed to drop index " + indexName + " on " + tableName + " - " + database.ErrorStateMessage);
}
@@ -240,7 +240,7 @@ namespace Sarand.Net.Standard.Dbas.Base
else
{
#if DEBUG
- SaLogger.Debug("DropDbIndex Table: " + tableName + "; Index: " + indexName + "; Exception: " + database.ErrorStateMessage);
+ SaLogger.Debug($"{SysUtil.Fn} Table: " + tableName + "; Index: " + indexName + "; Exception: " + database.ErrorStateMessage);
#endif
SetErrorState("Failed to drop primary key " + indexName + " on " + tableName + " - " + database.ErrorStateMessage);
}
@@ -288,7 +288,7 @@ namespace Sarand.Net.Standard.Dbas.Base
else
{
#if DEBUG
- SaLogger.Debug("CreatePrimaryDbIndex Table: %" + tableName + "; Index: " + indexName + "; Exception: " + database.ErrorStateMessage);
+ SaLogger.Debug($"{SysUtil.Fn} Table: %" + tableName + "; Index: " + indexName + "; Exception: " + database.ErrorStateMessage);
#endif
SetErrorState("Failed to create primary index " + indexName + " on " + tableName + " - " + database.ErrorStateMessage);
}
@@ -322,7 +322,7 @@ namespace Sarand.Net.Standard.Dbas.Base
else
{
#if DEBUG
- SaLogger.Debug("CreateDbIndex Table: " + tableName + "; Index: " + indexName + "; Exception: " + database.ErrorStateMessage);
+ SaLogger.Debug($"{SysUtil.Fn} Table: " + tableName + "; Index: " + indexName + "; Exception: " + database.ErrorStateMessage);
#endif
SetErrorState("Failed to create index " + indexName + " on " + tableName + " - " + database.ErrorStateMessage);
}
diff --git a/SacsStandardDbas/Dbas/CodeTypes/SaCodeTypeDbDfnGenerator.cs b/SacsStandardDbas/Dbas/CodeTypes/SaCodeTypeDbDfnGenerator.cs
index e8caf78..0b381e7 100644
--- a/SacsStandardDbas/Dbas/CodeTypes/SaCodeTypeDbDfnGenerator.cs
+++ b/SacsStandardDbas/Dbas/CodeTypes/SaCodeTypeDbDfnGenerator.cs
@@ -57,8 +57,8 @@ namespace Sarand.Net.Standard.Dbas.CodeType
OutputDirectory = directory,
};
// Schema - Tables.
- config.Schema.Tables_Included.AddRange(new String[] { "SaCodeType", "SaCode" });
- config.Schema.Tables_Excluded.AddRange(new String[] { });
+ config.Schema.Tables_Included.UnionWith(new String[] { "SaCodeType", "SaCode" });
+ config.Schema.Tables_Excluded.UnionWith(new String[] { });
// Schema - Columns.
config.Schema.InsertOnlyColumns.Add(".*", "CreateOn,CreateBy,CreateHost");
config.Schema.InsertOnlyColumns.Add("SaCodeType", "Code");
diff --git a/SacsStandardDbas/Dbas/Data/SacsDatabaseOdbc.cs b/SacsStandardDbas/Dbas/Data/SacsDatabaseOdbc.cs
index f62fcc7..81471d4 100644
--- a/SacsStandardDbas/Dbas/Data/SacsDatabaseOdbc.cs
+++ b/SacsStandardDbas/Dbas/Data/SacsDatabaseOdbc.cs
@@ -384,7 +384,7 @@ namespace Sarand.Net.Standard.Dbas.Data
// Only populate table names and/or table schemas where not previously done.
if (tables.Count < 1 || tables.Exists(item => !item.HasColumns()))
{
- // Use a single connection for table and schaema population.
+ // Use a single connection for table and schema population.
using (DbConnection dbConnection = CreateConnection(true))
{
dbConnection.Open();
diff --git a/SacsStandardDbas/Dbas/Interfaces/SaInterfaceDbDfnGenerator.cs b/SacsStandardDbas/Dbas/Interfaces/SaInterfaceDbDfnGenerator.cs
index baeabb0..25e757e 100644
--- a/SacsStandardDbas/Dbas/Interfaces/SaInterfaceDbDfnGenerator.cs
+++ b/SacsStandardDbas/Dbas/Interfaces/SaInterfaceDbDfnGenerator.cs
@@ -58,8 +58,8 @@ namespace Sarand.Net.Standard.Dbas.Interface
OutputDirectory = directory,
};
// Schema - Tables.
- config.Schema.Tables_Included.AddRange(new String[] { "SaInterface", "SaInterfaceProcess", "SaInterfaceProcessMessage", "SaInterfaceDataItem", "SaInterfaceProcessDataItem" });
- config.Schema.Tables_Excluded.AddRange(new String[] { });
+ config.Schema.Tables_Included.UnionWith(new String[] { "SaInterface", "SaInterfaceProcess", "SaInterfaceProcessMessage", "SaInterfaceDataItem", "SaInterfaceProcessDataItem" });
+ config.Schema.Tables_Excluded.UnionWith(new String[] { });
config.Schema.AddAlternativeTableName("SaInterface", "Interface");
config.Schema.AddAlternativeTableName("SaInterfaceProcess", "InterfaceProcess");
config.Schema.AddAlternativeTableName("SaInterfaceProcessMessage", "InterfaceProcessMessage");
diff --git a/SacsStandardDbas/Dbas/ItemData/SaItemDataDbDfnGenerator.cs b/SacsStandardDbas/Dbas/ItemData/SaItemDataDbDfnGenerator.cs
index 8b1d962..46a83d1 100644
--- a/SacsStandardDbas/Dbas/ItemData/SaItemDataDbDfnGenerator.cs
+++ b/SacsStandardDbas/Dbas/ItemData/SaItemDataDbDfnGenerator.cs
@@ -58,8 +58,8 @@ namespace Sarand.Net.Standard.Dbas.ItemData
OutputDirectory = directory,
};
// Schema - Tables.
- config.Schema.Tables_Included.AddRange(new String[] { "SaItemData" });
- config.Schema.Tables_Excluded.AddRange(new String[] { });
+ config.Schema.Tables_Included.UnionWith(new String[] { "SaItemData" });
+ config.Schema.Tables_Excluded.UnionWith(new String[] { });
// Schema - Columns.
config.Schema.InsertOnlyColumns.Add(".*", "CreateOn,CreateBy,CreateHost");
config.Schema.InsertOnlyColumns.Add("SaItemData", "ItemType,ItemId,Name,Context");
diff --git a/SacsStandardDbas/Dbas/ItemDocs/SaItemDocsDbDfnGenerator.cs b/SacsStandardDbas/Dbas/ItemDocs/SaItemDocsDbDfnGenerator.cs
index 42afc61..99edbea 100644
--- a/SacsStandardDbas/Dbas/ItemDocs/SaItemDocsDbDfnGenerator.cs
+++ b/SacsStandardDbas/Dbas/ItemDocs/SaItemDocsDbDfnGenerator.cs
@@ -62,8 +62,8 @@ namespace Sarand.Net.Standard.Dbas.ItemDocs
OutputDirectory = directory,
};
// Schema - Tables.
- config.Schema.Tables_Included.AddRange(new String[] { "SaItemDoc" });
- config.Schema.Tables_Excluded.AddRange(new String[] { });
+ config.Schema.Tables_Included.UnionWith(new String[] { "SaItemDoc" });
+ config.Schema.Tables_Excluded.UnionWith(new String[] { });
// Schema - Columns.
config.Schema.InsertOnlyColumns.Add(".*", "CreateOn,CreateBy,CreateHost");
config.Schema.InsertOnlyColumns.Add("SaItemDocs", "ItemType,ItemId,Name,Context");
diff --git a/SacsStandardDbas/Dbas/ItemOptions/SaItemOptionsDbDfnGenerator.cs b/SacsStandardDbas/Dbas/ItemOptions/SaItemOptionsDbDfnGenerator.cs
index a74607b..9593523 100644
--- a/SacsStandardDbas/Dbas/ItemOptions/SaItemOptionsDbDfnGenerator.cs
+++ b/SacsStandardDbas/Dbas/ItemOptions/SaItemOptionsDbDfnGenerator.cs
@@ -58,8 +58,8 @@ namespace Sarand.Net.Standard.Dbas.ItemOptions
OutputDirectory = directory,
};
// Schema - Tables.
- config.Schema.Tables_Included.AddRange(new String[] { "SaItemOption" });
- config.Schema.Tables_Excluded.AddRange(new String[] { });
+ config.Schema.Tables_Included.UnionWith(new String[] { "SaItemOption" });
+ config.Schema.Tables_Excluded.UnionWith(new String[] { });
// Schema - Columns.
config.Schema.InsertOnlyColumns.Add(".*", "CreateOn,CreateBy,CreateHost");
config.Schema.InsertOnlyColumns.Add("SaItemOption", "ItemType,ItemId,Name,Context");
diff --git a/SacsStandardDbas/Dbas/ReferenceData/SaReferenceDataDbDfnGenerator.cs b/SacsStandardDbas/Dbas/ReferenceData/SaReferenceDataDbDfnGenerator.cs
index b44d912..2f1aa3e 100644
--- a/SacsStandardDbas/Dbas/ReferenceData/SaReferenceDataDbDfnGenerator.cs
+++ b/SacsStandardDbas/Dbas/ReferenceData/SaReferenceDataDbDfnGenerator.cs
@@ -58,8 +58,8 @@ namespace Sarand.Net.Standard.Dbas.ReferenceData
OutputDirectory = directory,
};
// Schema - Tables.
- config.Schema.Tables_Included.AddRange(new String[] { "Language", "Region", "Currency", "TaxCode", "UnitMeasure" });
- config.Schema.Tables_Excluded.AddRange(new String[] { });
+ config.Schema.Tables_Included.UnionWith(new String[] { "Language", "Region", "Currency", "TaxCode", "UnitMeasure" });
+ config.Schema.Tables_Excluded.UnionWith(new String[] { });
// Schema - Columns.
config.Schema.InsertOnlyColumns.Add(".*", "CreateOn,CreateBy,CreateHost");
config.Schema.InsertOnlyColumns.Add("Language", "");
diff --git a/SacsStandardDbas/Dbas/Security/SaSecurityDbDfnGenerator.cs b/SacsStandardDbas/Dbas/Security/SaSecurityDbDfnGenerator.cs
index e4a3cf8..54b91c8 100644
--- a/SacsStandardDbas/Dbas/Security/SaSecurityDbDfnGenerator.cs
+++ b/SacsStandardDbas/Dbas/Security/SaSecurityDbDfnGenerator.cs
@@ -57,8 +57,8 @@ namespace Sarand.Net.Standard.Dbas.Security
OutputDirectory = directory,
};
// Schema - Tables.
- config.Schema.Tables_Included.AddRange(new String[] { "SaSecItem", "SaSecLink" });
- config.Schema.Tables_Excluded.AddRange(new String[] { });
+ config.Schema.Tables_Included.UnionWith(new String[] { "SaSecItem", "SaSecLink" });
+ config.Schema.Tables_Excluded.UnionWith(new String[] { });
config.Schema.AddAlternativeTableName("SaSecItem", "SaSecurityItem");
config.Schema.AddAlternativeTableName("SaSecLink", "SaSecurityLink");
// Schema - Columns.
diff --git a/SacsStandardDbas/Dbas/UpdateLog/SaUpdateLogDbDfnGenerator.cs b/SacsStandardDbas/Dbas/UpdateLog/SaUpdateLogDbDfnGenerator.cs
index 1a2cf18..220c102 100644
--- a/SacsStandardDbas/Dbas/UpdateLog/SaUpdateLogDbDfnGenerator.cs
+++ b/SacsStandardDbas/Dbas/UpdateLog/SaUpdateLogDbDfnGenerator.cs
@@ -58,8 +58,8 @@ namespace Sarand.Net.Standard.Dbas.UpdateLog
OutputDirectory = directory,
};
// Schema - Tables.
- config.Schema.Tables_Included.AddRange(new String[] { "SaAttrUpdate" });
- config.Schema.Tables_Excluded.AddRange(new String[] { });
+ config.Schema.Tables_Included.UnionWith(new String[] { "SaAttrUpdate" });
+ config.Schema.Tables_Excluded.UnionWith(new String[] { });
// SChema - Columns.
config.Schema.InsertOnlyColumns.Add(".*", "CreateOn,CreateBy,CreateHost");
config.Schema.InsertOnlyColumns.Add("SaAttrUpdate", "ItemType,ItemId,Logged,Attribute");
diff --git a/SacsStandardProc/Proc/Excel/SaMsSpreadsheet.cs b/SacsStandardProc/Proc/Excel/SaMsSpreadsheet.cs
index 7738189..18a4c4a 100644
--- a/SacsStandardProc/Proc/Excel/SaMsSpreadsheet.cs
+++ b/SacsStandardProc/Proc/Excel/SaMsSpreadsheet.cs
@@ -587,14 +587,14 @@ namespace Sarand.Net.Standard.Proc.Excel
public static int GetPosForColId(String colId)
{
colId = StringUtil.TrimWhitespace(StringUtil.OnBlank(colId)).ToUpper();
- if (!colId.Matches(new Regex("[A-Z]+")))
+ if (!colId.IsMatch("^[A-Z]{1,2}$"))
{
SaLogger.Warn(SysUtil.Fn + " Invalid column Id supplied: '" + colId + "'");
- return -1;
+ return 0;
}
- int col = (colId[0] - 'A');
+ int col = (colId[0] - 'A' + 1);
if (colId.Length > 1)
- col = ((colId[1] - 'A') * 26) + (colId[0] - 'A');
+ col = (col * 26) + (colId[1] - 'A' + 1);
return col;
}
diff --git a/SacsStandardProc/Proc/Excel/SaMsSpreadsheet_POI.cs b/SacsStandardProc/Proc/Excel/SaMsSpreadsheet_POI.cs
index 0fa0a68..1b3dc90 100644
--- a/SacsStandardProc/Proc/Excel/SaMsSpreadsheet_POI.cs
+++ b/SacsStandardProc/Proc/Excel/SaMsSpreadsheet_POI.cs
@@ -845,7 +845,7 @@ namespace Sarand.Net.Standard.Proc.Excel
try
{
if (IsCellDate(cell))
- return new DataValue(cell.DateCellValue);
+ return new DataValue((DateTime)(cell.DateCellValue ?? DateTime.MinValue));
if (IsCellNumeric(cell))
{
double value = cell.NumericCellValue;
@@ -971,7 +971,7 @@ namespace Sarand.Net.Standard.Proc.Excel
try
{
IRow row = GetRowByPos(rowPos, true);
- for (int valueIdx = 0; valueIdx < values.Count; valueIdx++)
+ for (int valueIdx = 0; valueIdx < values.Count; valueIdx++)
{
uint colPos = (uint)Math.Max(1, colPosStart + valueIdx);
Object value = values[valueIdx];
diff --git a/SacsStandardProc/Proc/Procs/SaProcess.cs b/SacsStandardProc/Proc/Procs/SaProcess.cs
index 1589d89..e1e0469 100644
--- a/SacsStandardProc/Proc/Procs/SaProcess.cs
+++ b/SacsStandardProc/Proc/Procs/SaProcess.cs
@@ -1,4 +1,6 @@
-using Sarand.Net.Standard.Base.Procs;
+using Sarand.Net.Standard.Base.App;
+using Sarand.Net.Standard.Base.Dbas;
+using Sarand.Net.Standard.Base.Procs;
using System;
using System.Collections.Generic;
using System.Text;
@@ -13,8 +15,11 @@ namespace Sarand.Net.Standard.Proc.Procs
// Definitions.
public new class Config : SaProcess_Base.Config
{
- protected Config() : base()
- { }
+ protected Config(DbUpdateInfo dbUpdateInfo) : base()
+ {
+ this.DbUpdateInfo = dbUpdateInfo;
+ }
+ public DbUpdateInfo DbUpdateInfo { get; }
}
//-------------------------------------------------------------------
diff --git a/SacsStandardProc/SacsStandardProc.csproj b/SacsStandardProc/SacsStandardProc.csproj
index 98bb737..0c39eae 100644
--- a/SacsStandardProc/SacsStandardProc.csproj
+++ b/SacsStandardProc/SacsStandardProc.csproj
@@ -10,8 +10,8 @@
-
-
+
+