using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Net.Mail; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Runtime.CompilerServices; using System.Data.Common; //using Serialize.Linq.Serializers; namespace InABox.Core { public enum DatabaseProvider { //LiteDb, //MongoDb, SQLite } //public class DateTimeJsonConverter : JsonConverter //{ // public override void WriteJson(JsonWriter writer, DateTime value, JsonSerializer serializer) // { // String result = String.Format("{0:o}", value); // writer.WriteValue(result); // } // public override DateTime ReadJson(JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer) // { // if (reader.Value is DateTime) // return (DateTime)reader.Value; // else // { // String value = reader.Value.ToString().Replace("\"",""); // DateTime.TryParse(value, out DateTime result); // return result; // } // } // public override bool CanRead => true; // public override bool CanWrite => true; //} //public class TimeSpanJsonConverter : JsonConverter //{ // public override void WriteJson(JsonWriter writer, TimeSpan value, JsonSerializer serializer) // { // TimeSpan timespan = (TimeSpan)value; // writer.WriteValue(timespan.Ticks.ToString()); // } // public override TimeSpan ReadJson(JsonReader reader, Type objectType, TimeSpan existingValue, bool hasExistingValue, JsonSerializer serializer) // { // Int64 ticks = Int64.Parse(reader.Value.ToString().Replace("\"", "")); // return new TimeSpan(ticks); // } //} public enum SyncfusionVersion { v16_3, v16_4, v17_1, v17_2, v17_4, v18_1, v18_4, v19_1, v19_3, v19_4, v20_1, v20_2, v20_3, v22_1, v23_1, v23_2, v25_2, v26_x, Unspecified } [LibraryInitializer] public static class CoreUtils { private static readonly Dictionary entities = new Dictionary(); public static Dictionary TypeListSummary = new Dictionary(); private static readonly ConcurrentDictionary _columnscache = new ConcurrentDictionary(); private static readonly Regex StripHtmlStyles = new Regex(@"", RegexOptions.Compiled); private static readonly Regex StripHtmlTags = new Regex(@"<(.|\n)*?>", RegexOptions.Compiled); private static readonly Regex StripSpecialCharacters = new Regex(@"&#(.|\n)*?;| |>|<"", RegexOptions.Compiled); private static readonly Regex StripLineBreaks = new Regex(@"( |\r?\n|\r|\n)\1+", RegexOptions.Compiled); /// /// Return all that have been loaded through use of the function. /// /// /// This includes every non-abstract type. /// public static IEnumerable Entities => entities.Values; public static long GenerateSequence() { return (DateTime.Now - new DateTime(2019, 1, 1)).Ticks; } #region Licensing public static string SyncfusionLicense(SyncfusionVersion version = SyncfusionVersion.Unspecified) { var licenses = new Dictionary { { SyncfusionVersion.v16_3, "MTE0MTMyQDMxMzYyZTMzMmUzMFYwZ1YvdG4vNWVlb2JmMGdLSWJVd2l3cmpWVU9nYXVLYmdZbXkzUkpWK3M9;MTE0MTMzQDMxMzYyZTMzMmUzMGFraS9ZQkgxdXlQdDJNdWVIaHFXS0I0S29HY1JrVERYT1hEUVpmczlCYW89;MTE0MTM0QDMxMzYyZTMzMmUzMGozZGw2bERQVHFVaUkrOWZ0Z0swNVQzWkM5OEdCN042VTNnUzdZWENMMk09;MTE0MTM1QDMxMzYyZTMzMmUzMERTYTIzVmlSKzllZnQ2T3ZTVXVLbEJmRnh2UUQ2d0pFb2JLRWVlcHdwMWc9;MTE0MTM2QDMxMzYyZTMzMmUzMFowVmxDMmlXUVVPVVowR2sxaWJxVVJ6M1NORW81clF2KzJhbXN3K2ZkZWs9;MTE0MTM3QDMxMzYyZTMzMmUzMEQxMnRUMjJOLzU3dTdwaGZzRmUvS0ZVRWVkc3dZMSszYWQ1ZkpueDZNS2c9;MTE0MTM4QDMxMzYyZTMzMmUzMENZcDRIS0N6U2psbmFiM1BIdGJzZUU4Y1lWWm5oNm52T1FsVmVDK3g5RG89;MTE0MTM5QDMxMzYyZTMzMmUzMEdjbHhYbExwU1VYblJGM3F4TC94R2cvd3J4MGJMc21qelpNbHg2cUZjckk9;MTE0MTQwQDMxMzYyZTMzMmUzMGNPZDFCcklTSkJVakFtVlhDdlgwdDgzeWNwZnliZmVqQUExUWNhRjhzVHc9;MTE0MTQxQDMxMzYyZTMzMmUzMFowVmxDMmlXUVVPVVowR2sxaWJxVVJ6M1NORW81clF2KzJhbXN3K2ZkZWs9" }, { SyncfusionVersion.v16_4, "MTE0MTQyQDMxMzYyZTM0MmUzMGJ4UzVnMEJkOFpUM2pqanhPbnUrclk3c3lzQjVMdnV0eExnb3ljdGhqUjQ9;MTE0MTQzQDMxMzYyZTM0MmUzME5pY1I4ZWI1TnIwVFZVQXlBYWlEb0lsVTcxc1RUWEVOMWxESUIzVEJ6QXM9;MTE0MTQ0QDMxMzYyZTM0MmUzMG8wWjY0bml1QWVDeXI0TkI3STZWZEs2TkJjOFNaQWxTUCttL2tSNVUzbjA9;MTE0MTQ1QDMxMzYyZTM0MmUzMFNvanpRenpST2FtWHNJbzFwcnZ0djhuRjd3NnlGcVA4N3ljWFpCcExDVjg9;MTE0MTQ2QDMxMzYyZTM0MmUzMFJXMkVxT29WN0tMWEhDbTl5Uko5ak12RjV0dU1SODlZcGY1TkFudmVnR009;MTE0MTQ3QDMxMzYyZTM0MmUzMGU1U0dpZm5UTVBCclQvVmxBQkhNUlhoWUw0cmw5WFpRK0hKNUFpVitlSVU9;MTE0MTQ4QDMxMzYyZTM0MmUzMFRZa1NvdzV4NUNYTDJHaVdnL1k0SEh6ZjhocTYwZUkrVmt4eVdKOEJNR289;MTE0MTQ5QDMxMzYyZTM0MmUzMEMyM21MSEZUdmo4Qk1WNGZWekVBSkE4V2Zva2ZrNnh0WmwvN20rTWxhZzg9;MTE0MTUwQDMxMzYyZTM0MmUzMGt2N0wvMml1MHp3MVNVblJSeDZTaXUwdGxvWmlxcXZ2blFJNzNJSDhRV1k9;MTE0MTUxQDMxMzYyZTM0MmUzMFJXMkVxT29WN0tMWEhDbTl5Uko5ak12RjV0dU1SODlZcGY1TkFudmVnR009" }, { SyncfusionVersion.v17_1, "MTE0MTUyQDMxMzcyZTMxMmUzMGJxNHN0ZU9tVmNWK0RCUlRDeXpMU0hnenBid2xiTTZKemVpTmRlTFJOT3c9;MTE0MTUzQDMxMzcyZTMxMmUzMG1Qak9CaW9yd1dVUUxvNDJoY3dFOE5lSGd6dGtNQlJ1am55d2twQkR4ZXM9;MTE0MTU0QDMxMzcyZTMxMmUzMFJXTDlNVnVQc25uL25QMFR2WVFMS054RUpVckFtUWljVjM2MkZCRkNLTDQ9;MTE0MTU1QDMxMzcyZTMxMmUzMEJ4YUJRc2dVSWJVaVlzWjN1a3FMdEZnQnpmTWd0cUlSS0dqK0RkRU9FSUk9;MTE0MTU2QDMxMzcyZTMxMmUzMEo2UGVucVZ3eVpkWmFJQnVqdnlLU0dERXlpdzBwb3IwcTNBMXBVbzNveVU9;MTE0MTU3QDMxMzcyZTMxMmUzME9BUXVITzhwK2tlay9qTmc5cXFzVEJ1ZUJvOUU0dmNDUXNYZ2FEQ1R3YzA9;MTE0MTU4QDMxMzcyZTMxMmUzMEpEUzFLNi91U2FocXNwRGdnL2xrZzdvYTd6OXBKRmI4S0RXckRoZENFZDA9;MTE0MTU5QDMxMzcyZTMxMmUzMGVxSk9wNDVOUGluZDhFYXpMWVZKTklCK0M5R29JMG5RTmw1T002T3YrUzA9;MTE0MTYwQDMxMzcyZTMxMmUzMEtQZnBMczBYWDJKSll5LzdqN21QcVRHd2hYeEgzMXkrM3JSdmNTVXpHYXM9;MTE0MTYxQDMxMzcyZTMxMmUzMEo2UGVucVZ3eVpkWmFJQnVqdnlLU0dERXlpdzBwb3IwcTNBMXBVbzNveVU9" }, { SyncfusionVersion.v17_2, "MTIwNTA0QDMxMzcyZTMyMmUzMGxuSUVyYmtQYWdYSFlsdmYyV29FYXRKNXJiOVlkSjhYQ0djS2NQazd5czA9;MTIwNTA1QDMxMzcyZTMyMmUzMGJEb3VvekNxaUFXa3N5b2l4eXpxZ3NqRkMvbnpjQjFUMHpWbmlMaktBSlU9" }, { SyncfusionVersion.v17_4, "MTgzNDc1QDMxMzcyZTM0MmUzMFhkYlgrZXhMYjVLMFY3MStXZTExTkRJMVhjRGg3S3B2TXRzTloxVHpHc289;MTgzNDc2QDMxMzcyZTM0MmUzME9wZ01lakhTT04zOGJybFp5KzVBYjdFamFqV2Jpd01Wckdnd1dTd3JUeDQ9" }, { SyncfusionVersion.v18_1, "MzU3NDMwQDMxMzgyZTMxMmUzME1VV29JM0Z2aVpZenBSaThtbmphSTBGZ0djNnoyUGNsZk5zU0dGVTR4TFE9;MzU3NDMxQDMxMzgyZTMxMmUzMGhsUUZJaXRmRjNkb1hCeWZiMG03QWo0S1UxRnEwRFhTbHUvN3FUWnZvRVU9" }, { SyncfusionVersion.v18_4, "NDI2MzEzQDMxMzgyZTM0MmUzMGxsVE9iRkMrckpDdjNZcUZYVGZCaHloNTFRQnd6c1FKSDc5ZWRYcWgrR0U9;NDI2MzE0QDMxMzgyZTM0MmUzME1JZzhPTC84b01kVGlzdDlmNUljMmZtblJBTVdHTHF6V00wZGpVNm5sWkE9" }, { SyncfusionVersion.v19_1, "NDI2MzA4QDMxMzkyZTMxMmUzMG9DaFBkRXRPKzZISTBLejVpYlBCM3hJVUxjNEgwMnBqdlZmTmRmOXVjdGM9;NDI2MzA5QDMxMzkyZTMxMmUzMGRoNDJVNDNKUldmbytZQ3dzTWJPY0VuZEMwT1phM09vMStEajhYMzZ2WGc9" }, { SyncfusionVersion.v19_3, "NTI0MzQxQDMxMzkyZTMzMmUzME44d0c4ME9lYmZGNW5nK3B0Vm1GQTNZeUFLeDhNSWh0SWZ0bzUxWHNEU3M9;NTI0MzQyQDMxMzkyZTMzMmUzMGpGdkdnQmtxbHdic0QvaktPa2pZcG0zMU5tV2NxZ3lhdks4N01HREJtYU09;NTI0MzQzQDMxMzkyZTMzMmUzME10dy9uUGp1OTJhc095S3RiQXhsclN5MUhUcVJNd1N2TEFCWThSWkp5Q289" }, { SyncfusionVersion.v19_4, "NTUwNDcyQDMxMzkyZTM0MmUzMG9QaUZGdHNGWVpXRU90clpIZ2JJVkVXU2Q2VHFRdEZMY1Z3U3RySUF3a1k9;NTUwNDczQDMxMzkyZTM0MmUzMGtzdlcwYmlwZUVzNTZsNUJQMFN3K1hMYXdmZVlVNms1N2RtcWFvWHdBczg9" }, { SyncfusionVersion.v20_1, "NjYxMzYzQDMyMzAyZTMxMmUzMGpLV2tKMkl1bjRXOVNRMkRjYkxlNFNXQ3g5OGI2V2RoWkNpSk9HY3dyakE9; NjYxMzY0QDMyMzAyZTMxMmUzMGFaK2JxYzhTVnpTdXNEckVEM2x5ckVXdTJkVThpV3ZZRVE0emI1ekl2eWM9" }, { SyncfusionVersion.v20_2, "Mgo+DSMBaFt/QHJqVVhjWlpFdEBBXHxAd1p/VWJYdVt5flBPcDwsT3RfQF9jTn9QdkZiXX9YcHxXQw==;Mgo+DSMBPh8sVXJ0S0R+XE9HcFRDX3xKf0x/TGpQb19xflBPallYVBYiSV9jS3xTcURgWXxfdHRTRmlYVA==;ORg4AjUWIQA/Gnt2VVhiQlFadVlJXGFWfVJpTGpQdk5xdV9DaVZUTWY/P1ZhSXxRdkJiWn9dcXBVQGFcVEE=;Njc1MTQ1QDMyMzAyZTMyMmUzMGdvdWNsV3podC9LTC9MamF2YXNpT3pjR3Rsa3NGQzBxdWNtSGUvV0dsOGc9;Njc1MTQ2QDMyMzAyZTMyMmUzMFdhQkYvdkFwa3NTWXo4MnkxVWY4VnlyMHJlbmVud3N6TWQ3dkN6b3RmVm89;NRAiBiAaIQQuGjN/V0Z+Xk9EaFxEVmJLYVB3WmpQdldgdVRMZVVbQX9PIiBoS35RdEVkWHxec3VRRmZdVkRz;Njc1MTQ4QDMyMzAyZTMyMmUzMFcyaWFYL0dGZXFwanNIMHFEcnZ1d2pSMWhQR21PWEVwdjJjMjRuUVQxYjA9;Njc1MTQ5QDMyMzAyZTMyMmUzMGJjWHhqbkRRYXJkd3RFV2FXbFlGREZVd2FuaFNXM21qa1dha1lNWC8rWDA9;Mgo+DSMBMAY9C3t2VVhiQlFadVlJXGFWfVJpTGpQdk5xdV9DaVZUTWY/P1ZhSXxRdkJiWn9dcXBVQGJfWEE=;Njc1MTUxQDMyMzAyZTMyMmUzMGNBTUpQbGxtS3E3NDV1M1pEUFVjMjR6eEg5S3ZiRGhUalVHNk5oenNybWs9;Njc1MTUyQDMyMzAyZTMyMmUzMGk1U1lKR3lKUTMwWEdJb2JVbG5WYWVkMUwwbFpPMG9SbSszZmt2MGRoZUU9;Njc1MTUzQDMyMzAyZTMyMmUzMFcyaWFYL0dGZXFwanNIMHFEcnZ1d2pSMWhQR21PWEVwdjJjMjRuUVQxYjA9" }, { SyncfusionVersion.v20_3, "NzY3MzEzQDMyMzAyZTMzMmUzMGR2bk5Bb1lOK2pJV2lLdys4YlVDMGViYUhCY21PM2xITTRWZlhYYmRlVm89;NzY3MzE0QDMyMzAyZTMzMmUzMG9vSEdIY1d1NDFPYjBGSnJMcTlQSmpMUzYwNk9HZUNXWHNJZWlXZloyT0U9" }, { SyncfusionVersion.v22_1, "MjU0OTIyM0AzMjMyMmUzMDJlMzBhMTc2SkUzUFkyUzcyUFVhNzFLWDRCOElLTkVjQ2JQOG9zZ09CUTBuZjN3PQ==;MjU0OTIyNEAzMjMyMmUzMDJlMzBtaG5naUZPVVRwdENBWGRNSDdYK3orQ3NCcjVkYjRVUzQ4ZHoyRkloeStVPQ==;MjU0OTIyNUAzMjMyMmUzMDJlMzBtaG5naUZPVVRwdENBWGRNSDdYK3orQ3NCcjVkYjRVUzQ4ZHoyRkloeStVPQ==" }, { SyncfusionVersion.v23_1, "MjgwNDUxMkAzMjMzMmUzMDJlMzBlYjFtTGpVVkFBVjB5K0VXWXJBazhXa3htWGFQMnJuK0txb0VTL2EycmlBPQ==;MjgwNDUxM0AzMjMzMmUzMDJlMzBXTC9sRzg4aFV1VU1uS1F5a1UrZHdFSzMxaFFsYVpPbDlsc0F5OVNEVFhJPQ==;MjgwNDUxNEAzMjMzMmUzMDJlMzBXTC9sRzg4aFV1VU1uS1F5a1UrZHdFSzMxaFFsYVpPbDlsc0F5OVNEVFhJPQ==" }, { SyncfusionVersion.v23_2, "Ngo9BigBOggjHTQxAR8/V1NHaF1cWmhIfEx1RHxQdld5ZFRHallYTnNWUj0eQnxTdEZiW35fcHRQRGVZWEBzVg==" }, { SyncfusionVersion.v25_2 , "MzMwNDU4OUAzMjM1MmUzMDJlMzBPYmxqTDFKaUlLVHgvTmdXZk5Lcm5tNkhJT0RtWUZwM1JxQ1ZLVXVJaDJvPQ==;MzMwNDU5MEAzMjM1MmUzMDJlMzBoamJkMDY0QWFhWnVCbEZDelFCZWtkYXFoTGp3anlEUThrSGlReGxBOXdjPQ==" }, { SyncfusionVersion.v26_x, "MzM0OTg1MUAzMjM2MmUzMDJlMzBCSy82eWowdW8rSUIzQmRseGlHTUx2WDNRVUJucnpQSGRiUUJEOWtISklNPQ==;MzM0OTg1MkAzMjM2MmUzMDJlMzBhL0lLS3FZMTNOL2dQZXVkTkNWM2psdU9obU1UVklyV2tVczFvcE13eHpNPQ==;MzM0OTg1M0AzMjM2MmUzMDJlMzBhL0lLS3FZMTNOL2dQZXVkTkNWM2psdU9obU1UVklyV2tVczFvcE13eHpNPQ==" }, { SyncfusionVersion.Unspecified, "NjcyNDdAMzEzNjJlMzQyZTMwbWZlcXhUQW80NjUrTENRTVM0c2J5empYZDFUcHhDcEE0T3gwZDZZVGVtQT0=;NjcyNDhAMzEzNjJlMzQyZTMwZWtuc0VlSXVteWNtUE8zQ1V1azcrZmRjTFBqUGFHenU1YzNwbUVTdlcxVT0=" } }; return licenses[version]; } public static void CheckLicensing() { var entities = Entities.Where(x => x.IsClass && !x.IsGenericType && x.IsSubclassOf(typeof(Entity))); foreach (var entity in entities) { if (!entity.HasInterface(typeof(ILicense<>))) Logger.Send(LogType.Error, "", "License Configuration Error: " + entity.EntityName().Split('.').Last()); } } #endregion public static Guid FullGuid = Guid.Parse("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"); #region Reflection public static bool HasInterface(this Type type, Type interfaceType) { return type.GetInterfaces(interfaceType).Any(); } public static bool HasInterface(this Type type) => type.HasInterface(typeof(T)); public static Type? GetInterfaceDefinition(this Type type, Type interfaceType) { return type.GetInterfaces(interfaceType).FirstOrDefault(); } public static IEnumerable GetInterfaces(this Type type, Type interfaceType) { return type .GetInterfaces() .Where(x => x.IsGenericType ? x.GetGenericTypeDefinition() == interfaceType : x == interfaceType); } public static IEnumerable GetInterfaceDefinitions(this Type type, Type interfaceType) { foreach(var inter in type.GetInterfaces()) { if (inter == interfaceType) { yield return inter; } else if (inter.IsGenericType) { var genericTypeDefinition = inter.GetGenericTypeDefinition(); if (genericTypeDefinition == interfaceType) yield return genericTypeDefinition; } } } public static bool IsSubclassOfRawGeneric(this Type toCheck, Type generic) { while (toCheck != typeof(object)) { var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck; if (generic == cur) return true; toCheck = toCheck.BaseType; } return false; } public static Type? GetSuperclassDefinition(this Type toCheck, Type generic) { while (toCheck != typeof(object)) { var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck; if (generic == cur) return toCheck; toCheck = toCheck.BaseType; } return null; } public static bool HasAttribute(this Type type) where T : Attribute => type.GetCustomAttribute() != null; #endregion #region Entities + Types public static void RegisterClasses() { RegisterClasses(typeof(CoreUtils).Assembly); ImportFactory.Register(typeof(CSVImporter<>), "Comma Separated", "Comma Separated Files (*.csv)|*.csv"); ImportFactory.Register(typeof(TabImporter<>), "Tab Delimited", "Tab-Delimited Files (*.txt)|*.txt"); ImportFactory.Register(typeof(FixedWidthImporter<>), "Fixed Width", "Text Files (*.txt)|*.txt"); } /// /// Register into all types in that are not abstract. /// /// public static void RegisterClasses(params Assembly[] assemblies) { foreach(var type in assemblies.SelectMany(x => IterateTypes(x)).Where(x => !x.IsAbstract)) { entities.TryAdd(type.EntityName(), type); } } /// /// Returns a namespaced name for , converting also generic arguments to an angle-bracketed form. /// /// /// public static string EntityName(this Type t) { if (t.GetTypeInfo().IsGenericType) { var basename = t.FullName.Split(new[] { "[[" }, StringSplitOptions.RemoveEmptyEntries)[0]; var types = new List(); foreach (var type in t.GenericTypeArguments) types.Add(type.EntityName()); var result = string.Format("{0}<{1}>", basename, string.Join(",", types)); return result; } if (string.IsNullOrEmpty(t.FullName)) return t.Name; return t.FullName.Split(',')[0]; } private static Type? FindType(string name) { return entities.GetValueOrDefault(name) ?? Type.GetType(name); } public static bool TryGetEntity(string? entityname, [NotNullWhen(true)] out Type? type) { if (string.IsNullOrWhiteSpace(entityname)) { type = null; return false; } if (entityname.Contains("<")) { var comps = entityname.Replace(">", "").Split('<'); var basetype = FindType(comps[0]); var types = new List(); var generics = comps[1].Split(','); foreach (var generic in generics) { var genericArg = FindType(generic); if (genericArg is null) { type = null; return false; } types.Add(genericArg); } type = basetype?.MakeGenericType(types.ToArray()); } else { type = FindType(entityname); } return type != null; } public static Type GetEntity(string entityname) { if (TryGetEntity(entityname, out var type)) return type; throw new Exception($"Entity {entityname} does not exist!"); } public static Type? GetEntityOrNull(string? entityname) { TryGetEntity(entityname, out var type); return type; } //private static List TypeCache = null; /// /// Returns a list of types that have been loaded by the various calls to RegisterClasses, e.g. /// CoreUtils.RegisterClasses /// /// /// public static List TypeList(Func Predicate) { var result = new List(); try { foreach (var type in entities.Values) try { if (Predicate(type)) result.Add(type); } catch (Exception e) { LogException("", e, "Error in CoreUtils.TypeList"); } } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } return result; } /// /// Returns a list of types present within the assemblies. /// /// /// /// public static IEnumerable IterateTypes(Assembly assembly) { Type[] types; try { types = assembly.GetTypes(); } catch (ReflectionTypeLoadException e0) { types = e0.Types; } catch (Exception) { types = Array.Empty(); } foreach (var type in types) { if (type != null) { yield return type; } } } /// /// Returns a list of types present within the assemblies. /// /// /// Prefer, when possible, . /// /// /// /// public static ICollection TypeList(IEnumerable assemblies, Func Predicate) { var result = new HashSet(); foreach (var assembly in assemblies) { try { foreach(var type in IterateTypes(assembly).Where(Predicate)) { result.Add(type); } } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } } return result; } #endregion public static object? ChangeType(object? value, Type type) { if ((value == null || value is DBNull) && type.GetTypeInfo().IsGenericType) return Activator.CreateInstance(type); if (value == null) return null; if (value is DBNull) return null; if (type == value.GetType()) return value; if (type == typeof(bool)) return value.Equals(bool.TrueString) || value.Equals(1) || value.Equals("1"); if (type.GetTypeInfo().IsEnum) { if (value.GetType().IsArray) return (value as Array).Cast().Select(x => Enum.ToObject(type, x)).ToArray(); if (value is string) return Enum.Parse(type, value as string); if (value is IEnumerable list) return list.Select(x => Enum.ToObject(type, x)).ToArray(); return Enum.ToObject(type, value); } if (!type.GetTypeInfo().IsInterface && type.GetTypeInfo().IsGenericType) { var innerType = type.GetTypeInfo().GetGenericArguments()[0]; var innerValue = ChangeType(value, innerType); return Activator.CreateInstance(type, innerValue); } if (value is string && type == typeof(Guid)) try { return new Guid(value as string); } catch { return Guid.Empty; } if (value is IEnumerable objList && type == typeof(Guid)) return objList.Select(x => ChangeType(x)).ToArray(); if (value is byte[] && type == typeof(Guid)) return new Guid(value as byte[]); if (value is string && type == typeof(Version)) return new Version(value as string); if (value is byte && type == typeof(TimeSpan)) return new TimeSpan((byte)value); if (value is string && type == typeof(TimeSpan)) return TimeSpan.Parse(value as string); if (value is string && type == typeof(DateTime)) return DateTime.Parse(value as string); if (value is bool && type == typeof(string)) return (bool)value ? bool.TrueString : bool.FalseString; if (value is JArray && type == typeof(string[])) return (value as JArray).Select(x => x.ToString()).ToArray(); if (value is string && type == typeof(byte[])) return Convert.FromBase64String(value as string); if(value is FilterConstant constant) { if (type == typeof(DateTime)) { if (Equals(FilterConstant.Null, value)) return DateTime.MinValue; return value; } if (type == typeof(Guid) && constant == FilterConstant.Null) { return Guid.Empty; } if (type == typeof(double)) { return value; // if (Equals(FilterConstant.Zero, value)) // return (double)0; } if (type == typeof(int)) { return value; // if (Equals(FilterConstant.Zero, value)) // return 0; } } if (type == typeof(String)) return value?.ToString() ?? string.Empty; if (!(value is IConvertible)) return value; return Convert.ChangeType(value, type); } [return: MaybeNull] public static T ChangeType(object? value) { var newValue = ChangeType(value, typeof(T)); if (newValue is T tValue) return tValue; return default; } public static string RandomHexString(int length) { var rdm = new Random((int)DateTime.Now.Ticks); var hexValue = ""; int num; for (var i = 0; i < length; i++) { num = rdm.Next(0, 16); hexValue += num.ToString("X1"); } return hexValue; } public static bool IsEmpty(this DateTime value) { return value.Ticks <= DateTime.MinValue.AddHours(8).Ticks; } #region Properties public static string GetFullPropertyName(Expression> exp, string separator) { try { if (!TryFindMemberExpression(exp.Body, out var memberExp)) return ""; return GetFullPropertyName(memberExp, separator); } catch (Exception e) { throw e; } } public static string GetFullPropertyName(MemberExpression exp, string separator) { try { var currentExpression = exp; var memberNames = new Stack(); do { memberNames.Push(currentExpression.Member.Name); } while (TryFindMemberExpression(currentExpression.Expression, out currentExpression)); var result = string.Join(separator, memberNames.ToArray()); return result; } catch (Exception e) { throw e; } } // code adjusted to prevent horizontal overflow public static bool TryFindMemberExpression(Expression exp, [NotNullWhen(true)] out MemberExpression? memberExp) { memberExp = exp as MemberExpression; if (memberExp != null) return true; if (IsConversion(exp) && exp is UnaryExpression unary) { memberExp = unary.Operand as MemberExpression; if (memberExp != null) return true; } return false; } public static Expression> GetPropertyExpression(string name) { var param = Expression.Parameter(typeof(T), "x"); Expression body = param; foreach (var member in name.Split('.')) body = Expression.PropertyOrField(body, member); var p = Expression.Parameter(typeof(TType)); var conversion = body.Type == typeof(TType) ? body : Expression.Convert(body, typeof(TType)); return Expression.Lambda>(conversion, param); } public static Expression> GetPropertyExpression(string name) { var param = Expression.Parameter(typeof(T), "x"); Expression body = param; foreach (var member in name.Split('.')) body = Expression.PropertyOrField(body, member); var p = Expression.Parameter(typeof(object)); var conversion = Expression.Convert(body, typeof(object)); return Expression.Lambda>(conversion, param); } public static MemberExpression GetMemberExpression(Type t, string name) { var param = Expression.Parameter(t, "x"); Expression body = param; foreach (var member in name.Split('.')) body = Expression.PropertyOrField(body, member); var me = Expression.Lambda(body, param).Body as MemberExpression; return me; } public static Expression GetPropertyExpression(Type t, string name, Type? type = null) { var param = Expression.Parameter(t, "x"); Expression body = param; foreach (var member in name.Split('.')) body = Expression.PropertyOrField(body, member); if (type != null) body = Expression.Convert(body, typeof(object)); return Expression.Lambda(body, param); //var p2 = Expression.Parameter(t, "x"); //Expression e2 = Expression.Lambda(Expression.Property(p2, name), p2); //return e2; } public static PropertyInfo GetProperty(Type t, string name) { if (TryGetProperty(t, name, out var info)) return info; throw new Exception($"Property {name} does not exist for {t.Name}"); } public static PropertyInfo GetProperty(string name) => GetProperty(typeof(T), name); public static bool TryGetProperty(Type t, string name, [NotNullWhen(true)] out PropertyInfo? propertyInfo) { propertyInfo = null; var typeinfo = t.GetTypeInfo(); var props = name.Split('.'); foreach (var prop in props) { propertyInfo = typeinfo.GetProperty(prop); if (propertyInfo is null) return false; typeinfo = propertyInfo.PropertyType.GetTypeInfo(); } if (propertyInfo is null) return false; return true; } public static bool TryGetProperty(string name, [NotNullWhen(true)] out PropertyInfo? propertyInfo) => TryGetProperty(typeof(T), name, out propertyInfo); public static bool HasProperty(Type t, string name) { PropertyInfo property; var typeinfo = t.GetTypeInfo(); var props = name.Split('.'); foreach (var prop in props) { property = typeinfo.GetProperty(prop); if (property == null) return false; typeinfo = property.PropertyType.GetTypeInfo(); } return true; } public static object? GetPropertyParent(object o, string name) { var comps = name.Split('.').ToList(); comps.Remove(comps.Last()); if (comps.Count == 0) return o; return GetPropertyValue(o, string.Join(".", comps)); } /// /// Gets the value of the object's given property. /// /// The object to get the property from. /// The name of the property. /// /// if or any intermediate properties are ; /// otherwise the value of the property , which can be . /// /// if the property does not exist. public static object? GetPropertyValue(object? o, string name) { if (o is null) return null; var t = o.GetType().GetTypeInfo(); if (o is BaseObject) { var property = DatabaseSchema.Property(t, name); if (property != null && (property is CustomProperty || !name.Contains("."))) return property.Getter().Invoke(o); } var props = name.Split('.'); foreach (var prop in props) { var rprop = t.GetProperty(prop) ?? throw new Exception(string.Format("Property {0} does not exist for {1}", prop, o.GetType().Name)); o = rprop.GetValue(o, null); if (o is null) return null; t = o.GetType().GetTypeInfo(); } return o; } /// /// Sets a property on the given object, doing nothing if the property does not exist. /// /// The object to set the property of. /// The property to set. /// The value to set the property to. public static void SetPropertyValue(object o, string name, object? value) { var bits = name.Split('.'); for (var i = 0; i < bits.Length - 1; i++) { var propertyToGet = o.GetType().GetTypeInfo().GetProperty(bits[i]); if (propertyToGet == null) return; var newo = propertyToGet.GetValue(o, null); if (newo == null) { newo = Activator.CreateInstance(propertyToGet.PropertyType); propertyToGet.SetValue(o, newo); } o = newo; } var propertyToSet = o.GetType().GetTypeInfo().GetProperty(bits.Last()); if (propertyToSet is null) { var iprop = DatabaseSchema.Property(o.GetType(), name); if (iprop is CustomProperty) { ((BaseObject)o).UserProperties[name] = value; return; } return; } //PropertyInfo propertyToSet = GetProperty(o.GetType(), name); object? convertedvalue = null; if (value != null) { if (propertyToSet.PropertyType.Equals(typeof(Type))) { convertedvalue = Type.GetType(value.ToString(), true, true); } else if (propertyToSet.PropertyType.GetTypeInfo().IsEnum) { convertedvalue = Enum.Parse(propertyToSet.PropertyType, value.ToString(), true); } //else if (propertyToSet.PropertyType.Equals(typeof(Memo))) // convertedvalue = (Memo)value.ToString(); else if (propertyToSet.PropertyType.Equals(typeof(Guid))) { var guidvalue = Guid.Empty; Guid.TryParse(value.ToString(), out guidvalue); convertedvalue = guidvalue; } //else if (propertyToSet.PropertyType.IsSubclassOf(value.GetType())) // convertedvalue = value; else if (propertyToSet.PropertyType.IsAssignableFrom(value.GetType())) { convertedvalue = value; } else if (!(value is DBNull)) { convertedvalue = Convert.ChangeType(value, propertyToSet.PropertyType); } } if (propertyToSet.CanWrite) { var method = propertyToSet.GetSetMethod(); //if (method == null) var oldval = propertyToSet.GetValue(o); propertyToSet.SetValue(o, convertedvalue, null); if (o is BaseObject) ((BaseObject)o).OnPropertyChanged(propertyToSet.Name, oldval, convertedvalue); //else // method.Invoke(o, new Object[] { convertedvalue }); //if (o.GetType().IsSubclassOf(typeof(BaseObject))) // ((BaseObject)o).OnPropertyChanged(name, propertyToSet.GetValue(o), convertedvalue); } } public static PropertyInfo[] GetInheritedProperties(Type type) { if (type.IsInterface) { var propertyInfos = new List(); var considered = new List(); var queue = new Queue(); considered.Add(type); queue.Enqueue(type); while (queue.Count > 0) { var subType = queue.Dequeue(); foreach (var subInterface in subType.GetInterfaces()) { if (considered.Contains(subInterface)) continue; considered.Add(subInterface); queue.Enqueue(subInterface); } var typeProperties = subType.GetProperties( BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance); var newPropertyInfos = typeProperties .Where(x => !propertyInfos.Contains(x)); propertyInfos.InsertRange(0, newPropertyInfos); } return propertyInfos.ToArray(); } return type.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance); } public static Dictionary PropertyList(Type T, Func Predicate, bool Recursive) { Dictionary properties; if (T.GetTypeInfo().BaseType != null) properties = PropertyList(T.GetTypeInfo().BaseType, Predicate, Recursive); else properties = new Dictionary(); //TypeInfo entitytype = typeof(Entity).GetTypeInfo(); //TypeInfo componenttype = typeof(EnclosedEntity).GetTypeInfo(); foreach (var prop in T.GetRuntimeProperties()) // '.' contains check for explicit implementation of interface properties if (!properties.ContainsKey(prop.Name) && Predicate(prop) && !prop.Name.Contains('.')) { var propertytype = prop.PropertyType.GetTypeInfo(); if ( Recursive && ( propertytype.IsSubclassOf(typeof(Entity)) || propertytype.GetInterfaces().Contains(typeof(IEnclosedEntity)) || propertytype.GetInterfaces().Contains(typeof(IEntityLink)) ) ) { var props = PropertyList(prop.PropertyType, Predicate, Recursive); foreach (var key in props.Keys) properties[string.Format("{0}.{1}", prop.Name, key)] = props[key]; } else { properties[prop.Name] = prop.PropertyType; } } return properties; } public static Dictionary PropertyInfoList(Type T, Func Predicate, bool Recursive) { Dictionary properties; if (T.GetTypeInfo().BaseType != null) properties = PropertyInfoList(T.GetTypeInfo().BaseType, Predicate, Recursive); else properties = new Dictionary(); //TypeInfo entitytype = typeof(Entity).GetTypeInfo(); //TypeInfo componenttype = typeof(EnclosedEntity).GetTypeInfo(); foreach (var prop in T.GetRuntimeProperties()) // '.' contains check for explicit implementation of interface properties if (!properties.ContainsKey(prop.Name) && Predicate(prop) && !prop.Name.Contains('.')) { var propertytype = prop.PropertyType.GetTypeInfo(); if ( Recursive && ( propertytype.IsSubclassOf(typeof(Entity)) || propertytype.GetInterfaces().Contains(typeof(IEnclosedEntity)) || propertytype.GetInterfaces().Contains(typeof(IEntityLink)) ) ) { var props = PropertyInfoList(prop.PropertyType, Predicate, Recursive); foreach (var key in props.Keys) properties[string.Format("{0}.{1}", prop.Name, key)] = props[key]; } else { properties[prop.Name] = prop; } } return properties; } public static IEnumerable PropertyList(Type T, Func Predicate) { return T.GetRuntimeProperties().Where(x => Predicate(x)); } private static PropertyInfo InternalGetPropertyFromExpression(Expression> GetPropertyLambda) { MemberExpression Exp; //this line is necessary, because sometimes the expression comes in as Convert(originalexpression) if (GetPropertyLambda.Body is UnaryExpression unary) { Exp = unary.Operand as MemberExpression ?? throw new ArgumentException(); } else if (GetPropertyLambda.Body is MemberExpression) { Exp = (MemberExpression)GetPropertyLambda.Body; } else { throw new ArgumentException(); } return (PropertyInfo)Exp.Member; } public static PropertyInfo GetPropertyFromExpression(Expression> lambda) { return InternalGetPropertyFromExpression(lambda); } public static PropertyInfo GetPropertyFromExpression(Expression> GetPropertyLambda) { MemberExpression Exp; //this line is necessary, because sometimes the expression comes in as Convert(originalexpression) if (GetPropertyLambda.Body is UnaryExpression) { var UnExp = (UnaryExpression)GetPropertyLambda.Body; if (UnExp.Operand is MemberExpression) Exp = (MemberExpression)UnExp.Operand; else throw new ArgumentException(); } else if (GetPropertyLambda.Body is MemberExpression) { Exp = (MemberExpression)GetPropertyLambda.Body; } else { throw new ArgumentException(); } return (PropertyInfo)Exp.Member; } #endregion public static bool IsCollection(PropertyInfo prop) { if (!typeof(string).Equals(prop.PropertyType)) if (typeof(IEnumerable).GetTypeInfo().IsAssignableFrom(prop.PropertyType)) if (!prop.PropertyType.IsArray) if (prop.PropertyType.GenericTypeArguments[0].GetTypeInfo().IsSubclassOf(typeof(Entity))) return true; return false; } private static bool IsConversion(Expression exp) { return exp.NodeType == ExpressionType.Convert || exp.NodeType == ExpressionType.ConvertChecked; } //public static Dictionary PropertyList(Type T, bool Public = true, bool Static = false) //{ // Dictionary properties = new Dictionary(); // foreach (PropertyInfo prop in T.GetRuntimeProperties()) // { // if ((prop.GetMethod.IsPublic == Public) && (prop.GetMethod.IsStatic == Static)) // { // if (prop.PropertyType.GetTypeInfo().IsAssignableFrom(typeof(Entity).GetTypeInfo())) // { // Dictionary props = PropertyList(prop.PropertyType); // foreach (String key in props.Keys) // { // properties[String.Format("{0}.{1}", prop.Name, key)] = props[key]; // } // } // else if (!prop.PropertyType.Equals(typeof(Guid))) // { // properties[prop.Name] = prop.GetType(); // } // } // } // return properties; //} /// /// Get all the child properties of an object that match a given type /// /// The class of property to search for /// The object to search /// A list of the objects found public static IEnumerable GetChildrenProperties(Type parentType) { return PropertyList( parentType, x => typeof(T).IsAssignableFrom(x.PropertyType) ); } /// /// Get all the child properties of an object that match a given type /// /// The class of property to search for /// The object to search /// A list of the objects found public static IEnumerable GetChildren(object parent) { var props = PropertyList( parent.GetType(), x => typeof(T).IsAssignableFrom(x.PropertyType) ); foreach (var prop in props) { var child = (T)prop.GetValue(parent); if (child != null) yield return child; } } /// /// Get all list properties of an object that are collections of a given type /// /// The class of child object to search for /// The object to search /// A list of the collections found public static IEnumerable[] GetCollectionsOfType(object parent) { var lists = new List>(); var props = PropertyList(parent.GetType(), x => true); // !typeof(String).Equals(x.PropertyType)); foreach (var prop in props) if (typeof(IEnumerable).GetTypeInfo().IsAssignableFrom(prop.PropertyType)) if (prop.PropertyType.GenericTypeArguments.Length == 1) if (prop.PropertyType.GenericTypeArguments[0].GetTypeInfo().GetInterfaces().Contains(typeof(TType))) lists.Add((IEnumerable)prop.GetValue(parent)); return lists.ToArray(); } //public static IEnumerable GetReferencingAssemblies(string assemblyName) //{ // var assemblies = new List(); // var dependencies = DependencyContext.Default.RuntimeLibraries; // foreach (var library in dependencies) // { // if (IsCandidateLibrary(library, assemblyName)) // { // var assembly = Assembly.Load(new AssemblyName(library.Name)); // assemblies.Add(assembly); // } // } // return assemblies; //} //private static bool IsCandidateLibrary(RuntimeLibrary library, assemblyName) //{ // return library.Name == (assemblyName) // || library.Dependencies.Any(d => d.Name.StartsWith(assemblyName)); //} //public static IEnumerable AssemblyList(Expression> Predicate) //{ // Func lambda = Predicate.Compile(); // List assemblies = new List(); // IEnumerable allassemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => !a.IsDynamic).ToArray(); // foreach (Assembly assembly in allassemblies) // { // IEnumerable types = CoreUtils.TypeList( // new Assembly[] { assembly }, // Predicate // ); // if (types.Count() > 0) // assemblies.Add(assembly); // } // return assemblies; //} //public static string SerializeExpression(Expression obj) //{ // JsonSerializerSettings _settings; // var defaultSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects }; // defaultSettings.Converters.Add(new ExpressionJsonConverter(typeof(T))); // _settings = defaultSettings; // return Serialization.Serialize(obj, _settings); //} //public static Expression DeserializeExpression(string json) //{ // JsonSerializerSettings _settings; // var defaultSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects }; // defaultSettings.Converters.Add(new ExpressionJsonConverter(typeof(T))); // _settings = defaultSettings; // return Serialization.Deserialize(json, _settings); //} /// /// [ public static object GetDefault(this Type type) ] /// /// Retrieves the default value for a given Type /// /// The Type for which to get the default value /// The default value for /// /// If a null Type, a reference Type, or a System.Void Type is supplied, this method always returns null. If a value /// type /// is supplied which is not publicly visible or which contains generic parameters, this method will fail with an /// exception. /// /// /// To use this method in its native, non-extension form, make a call like: /// /// object Default = DefaultValue.GetDefault(someType); /// /// To use this method in its Type-extension form, make a call like: /// /// object Default = someType.GetDefault(); /// /// /// public static object? GetDefault(this Type type) { // If no Type was supplied, if the Type was a reference type, or if the Type was a System.Void, return null if (type == null || !type.GetTypeInfo().IsValueType || type == typeof(void)) return null; // If the supplied Type has generic parameters, its default value cannot be determined if (type.GetTypeInfo().ContainsGenericParameters) throw new ArgumentException( "Error:\n\nThe supplied value type <" + type.Name + "> contains generic parameters, so the default value cannot be retrieved\n\n"); // If the Type is a primitive type, or if it is another publicly-visible value type (i.e. struct/enum), return a // default instance of the value type if (type.GetTypeInfo().IsPrimitive || !type.GetTypeInfo().IsNotPublic) try { return Activator.CreateInstance(type); } catch (Exception e) { throw new ArgumentException( "Error:\n\nThe Activator.CreateInstance method could not " + "create a default instance of the supplied value type <" + type.Name + "> (Inner Exception message: \"" + e.Message + "\")"); } // Fail with exception throw new ArgumentException("Error:\n\nThe supplied value type <" + type + "> is not a publicly-visible type, so the default value cannot be retrieved"); } /// /// [ public static T GetDefault< T >() ] /// /// Retrieves the default value for a given Type /// /// The Type for which to get the default value /// The default value for Type T /// /// If a reference Type or a System.Void Type is supplied, this method always returns null. If a value type /// is supplied which is not publicly visible or which contains generic parameters, this method will fail with an /// exception. /// /// [return: MaybeNull] public static T GetDefault() { return (T)GetDefault(typeof(T)); } public static bool IsDefault(T value) { return IsDefault(typeof(T), value); } /// /// [ public static bool IsObjectSetToDefault(this Type ObjectType, object ObjectValue) ] /// /// Reports whether a value of type T (or a null reference of type T) contains the default value for that Type /// /// /// Reports whether the object is empty or unitialized for a reference type or nullable value type (i.e. is null) or /// whether the object contains a default value for a non-nullable value type (i.e. int = 0, bool = false, etc.) /// /// NOTE: For non-nullable value types, this method introduces a boxing/unboxing performance penalty. /// /// Type of the object to test /// The object value to test, or null for a reference Type or nullable value Type /// /// true = The object contains the default value for its Type. /// /// false = The object has been changed from its default value. /// public static bool IsDefault(this Type ObjectType, object? ObjectValue) { // If no ObjectType was supplied, attempt to determine from ObjectValue if (ObjectType == null) { // If no ObjectValue was supplied, abort if (ObjectValue == null) throw new ArgumentNullException("Error:\n\nCannot determine the ObjectType from a null Value"); // Determine ObjectType from ObjectValue ObjectType = ObjectValue.GetType(); } // Get the default value of type ObjectType var Default = ObjectType.GetDefault(); // If a non-null ObjectValue was supplied, compare Value with its default value and return the result if (ObjectValue != null) return ObjectValue.Equals(Default); // Since a null ObjectValue was supplied, report whether its default value is null return Default == null; } //public static TimeSpan Round(this TimeSpan timespan, TimeSpan interval) //{ // // The round number, here is a quarter... in seconds // double Round = interval.TotalSeconds; // // Count of round number in this total Seconds... // double CountRound = (timespan.TotalSeconds / Round); // // The main formula to calculate round time... // int Sec = (int)(Math.Truncate(CountRound + 0.5) * Round); // // Now show the result... // TimeSpan tRes = new TimeSpan(0, 0, Sec); // return tRes; //} public static bool IsNumeric(this Type dataType) { return dataType.IsFloatingPoint() || dataType.IsOrdinal(); } public static bool IsFloatingPoint(this Type dataType) { if (dataType == null) return false; return dataType == typeof(double) || dataType == typeof(float) || dataType == typeof(float); } public static bool IsOrdinal(this Type dataType) { if (dataType == null) return false; return dataType == typeof(int) || dataType == typeof(long) || dataType == typeof(short) || dataType == typeof(short) || dataType == typeof(int) || dataType == typeof(long) || dataType == typeof(uint) || dataType == typeof(ushort) || dataType == typeof(uint) || dataType == typeof(ulong) || dataType == typeof(sbyte); } //Extension method for MailMessage to save to a file on disk public static string GenerateEMLFile(this MailMessage message, bool addUnsentHeader = true) { var filename = Path.ChangeExtension(Path.GetTempFileName(), ".eml"); using (var filestream = File.Open(filename, FileMode.Create)) { if (addUnsentHeader) { var binaryWriter = new BinaryWriter(filestream); //Write the Unsent header to the file so the mail client knows this mail must be presented in "New message" mode binaryWriter.Write(Encoding.UTF8.GetBytes("X-Unsent: 1" + Environment.NewLine)); } var assembly = typeof(SmtpClient).Assembly; var mailWriterType = assembly.GetType("System.Net.Mail.MailWriter"); // Get reflection info for MailWriter contructor var mailWriterContructor = mailWriterType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(Stream) }, null); // Construct MailWriter object with our FileStream var mailWriter = mailWriterContructor.Invoke(new object[] { filestream }); // Get reflection info for Send() method on MailMessage var sendMethod = typeof(MailMessage).GetMethod("Send", BindingFlags.Instance | BindingFlags.NonPublic); sendMethod.Invoke(message, BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { mailWriter, true, true }, null); // Finally get reflection info for Close() method on our MailWriter var closeMethod = mailWriter.GetType().GetMethod("Close", BindingFlags.Instance | BindingFlags.NonPublic); // Call close method closeMethod.Invoke(mailWriter, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { }, null); } return filename; } public static Columns GetColumns(Columns? columns) => (GetColumns(typeof(T), columns) as Columns)!; public static IColumns GetColumns(Type T, IColumns? columns) { //var cols = columns; if (columns == null || columns.Count == 0) { if (!_columnscache.TryGetValue(T, out var result)) { result = Columns.None(T); var props = PropertyList(T, x => x.GetCustomAttribute() == null && x.GetCustomAttribute() == null && x.CanWrite && x.PropertyType != typeof(UserProperties), true); foreach (var prop in props) result.Add(prop.Key); var custom = DatabaseSchema.Properties(T).Where(x => x is CustomProperty); foreach (var prop in custom) result.Add(prop.Name); _columnscache[T] = result; } return result; } return columns; //{ // foreach (var col in cols.Items) // { // try // { // result.Add(col.Property); // //PropertyInfo prop = null; // //IProperty iprop = null; // //if (col.Property != null) // //{ // // try // // { // // iprop = DatabaseSchema.Property(typeof(T), col.Property); // // } // // catch (Exception e2) // // { // // } // // if ((iprop != null) && (iprop is CustomProperty)) // // result.Add(col.Property); // // else // // { // // prop = CoreUtils.GetProperty(typeof(T), col.Property); // // if ((prop.GetCustomAttribute() == null) && (prop.GetCustomAttribute() == null) && prop.CanWrite) // // result.Add(col.Property); // // } // //} // //else // //{ // // String property = CoreUtils.GetFullPropertyName((MemberExpression)col.Expression, "."); // // prop = CoreUtils.GetProperty(typeof(T), property); // // if ((prop.GetCustomAttribute() == null) && (prop.GetCustomAttribute() == null) && prop.CanWrite) // // result.Add(property); // //} // } // catch (Exception e) // { // } // } //} //return result; } public static List GetColumnNames(Type T, Func Predicate) { //var cols = columns; var result = new List(); var props = PropertyList(T, x => x.GetCustomAttribute() == null && x.GetCustomAttribute() == null && x.CanWrite && x.PropertyType != typeof(UserProperties) && Predicate(x), true); foreach (var prop in props) result.Add(prop.Key); var custom = DatabaseSchema.Properties(T).Where(x => x is CustomProperty); foreach (var prop in custom) result.Add(prop.Name); return result; } public static string TypedValueToString(object value) { var type = value.GetType(); var Culture = CultureInfo.CurrentCulture; if (type == typeof(string)) return (string)value; if (type == typeof(string[])) return Serialization.Serialize(value); if (IsNumeric(type)) return string.Format(Culture.NumberFormat, "{0}", value); // TimeSpan - use constant pattern if (type == typeof(TimeSpan)) return string.Format("{0:c}", value); // DateTime - use round-trip pattern if (type == typeof(DateTime)) return string.Format("{0:o}", value); if (type == typeof(Guid)) return ((Guid)value).ToString(); var converter = TypeDescriptor.GetConverter(type); if (converter != null && converter.CanConvertTo(typeof(string))) return converter.ConvertToString(value); throw new Exception(string.Format("Cannot Convert {0} to String", type.Name)); } public static object? StringToTypedValue(string value, Type type) { var Culture = CultureInfo.CurrentCulture; if (type == typeof(object) && string.IsNullOrEmpty(value)) return null; if (type == typeof(string)) return value; if (type == typeof(string[])) return Serialization.Deserialize(value); if (type == typeof(sbyte)) return sbyte.Parse(value, NumberStyles.Integer, Culture); if (type == typeof(byte)) return byte.Parse(value, NumberStyles.Integer, Culture); if (type == typeof(ushort)) return ushort.Parse(value, NumberStyles.Integer, Culture); if (type == typeof(uint)) return uint.Parse(value, NumberStyles.Integer, Culture); if (type == typeof(ulong)) return ulong.Parse(value, NumberStyles.Integer, Culture); if (type == typeof(short)) return short.Parse(value, NumberStyles.Integer, Culture); if (type == typeof(int)) return int.Parse(value, NumberStyles.Integer, Culture); if (type == typeof(long)) return long.Parse(value, NumberStyles.Integer, Culture); if (type == typeof(float)) return float.Parse(value, NumberStyles.Any, Culture); if (type == typeof(double)) return double.Parse(value, NumberStyles.Any, Culture); if (type == typeof(decimal)) return decimal.Parse(value, NumberStyles.Any, Culture); if (type == typeof(TimeSpan)) return TimeSpan.ParseExact(value, "c", null); if (type == typeof(DateTime)) return DateTime.ParseExact(value, "o", null); if (type == typeof(Guid)) return Guid.Parse(value); var converter = TypeDescriptor.GetConverter(type); if (converter != null && converter.CanConvertFrom(typeof(string))) return converter.ConvertFromString(value); throw new Exception(string.Format("Cannot Convert String to {0}", type.Name)); } public static bool ContainsInheritedGenericType(this Type type, Type generic) { if (type == null) return false; if (type.GenericTypeArguments.Contains(generic)) return true; if (type.BaseType == null) return false; return type.BaseType.ContainsInheritedGenericType(generic); } public static IEnumerable GetInheritedGenericTypeArguments(this Type type) { if (type == null) return new Type[] { }; if (type.GetGenericArguments().Any()) return type.GenericTypeArguments; if (type.BaseType == null) return new Type[] { }; return type.BaseType.GetInheritedGenericTypeArguments(); } //static readonly Regex StripQuotes = new Regex(@""", RegexOptions.Compiled); public static string GetCaption(this Type type, bool usedefault = true) { var attribute = type.GetCustomAttribute(); var result = attribute != null ? attribute.Text : usedefault ? type.Name : ""; return result; } public static string? GetCaptionOrNull(this Type type, bool inherit = false) { return type.GetCustomAttribute(inherit)?.Text; } public static string SanitiseFileName(string filename) { var invalidChars = Regex.Escape(new string(Path.GetInvalidFileNameChars())); var invalidReStr = string.Format(@"[{0}]+", invalidChars); var reservedWords = new[] { "CON", "PRN", "AUX", "CLOCK$", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" }; var sanitisedNamePart = Regex.Replace(filename, invalidReStr, "_"); foreach (var reservedWord in reservedWords) { var reservedWordPattern = string.Format("^{0}\\.", reservedWord); sanitisedNamePart = Regex.Replace(sanitisedNamePart, reservedWordPattern, "_reservedWord_.", RegexOptions.IgnoreCase); } return sanitisedNamePart; } public static decimal GetPropertySequence(Type type, string column) { return DatabaseSchema.Property(type, column)?.PropertySequence() ?? 999; } /// /// Gets the AppData folder for this application. /// /// The path of the AppData folder. public static string GetPath() { if (Assembly.GetEntryAssembly() != null) return Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location) ); return Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location) ); } public static string GetCommonAppData(string? folderName = null) { folderName ??= Assembly.GetEntryAssembly() != null ? Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location) : Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location); return Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), folderName ); } public static string GetVersion() { // 2022-10-13 FVB First look in the executable directory var curfile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "version.txt"); if (File.Exists(curfile)) return File.ReadAllText(curfile); // Then look in the app data directory if you cannot find it curfile = Path.Combine(GetPath(), "version.txt"); if (File.Exists(curfile)) return File.ReadAllText(curfile); return "???"; } public static string SplitCamelCase(this string value) { return string.Join(" ", Regex.Split(value, @"(? threshold; } public static bool IsEffectivelyLessThan(this double a, double b, double threshold = 0.00001F) { return b - a > threshold; } public static bool IsEntityLinkValid(this CoreRow arg, Expression> expression) where U : IEntityLink { var col = CoreUtils.GetFullPropertyName(expression, "."); return arg.Get(col + ".ID") != Guid.Empty && arg.Get(col + ".Deleted") == Guid.Empty; } /// /// Gets the ID of an entity link of an entity, doing a validity check (see ) /// /// The entity type /// The entity link type /// The row representing the entity of type /// An expression to the entity link of type /// The ID on the entity link, or null if the entity link is invalid public static Guid? EntityLinkID(this CoreRow arg, Expression> expression) where U : IEntityLink { var col = CoreUtils.GetFullPropertyName(expression, "."); var id = arg.Get(col + ".ID"); if (id != Guid.Empty && arg.Get(col + ".Deleted") == Guid.Empty) return id; return null; } public static Filter LinkValid(this Filter filter, Guid entityID = default) { var mExp = filter.Expression as MemberExpression; if (mExp == null) throw new ArgumentException("Filter expression is not a MemberExpression"); var idExp = Expression.PropertyOrField(mExp, "ID"); var deletedExp = Expression.PropertyOrField(mExp, "Deleted"); filter.Expression = idExp; if (entityID != Guid.Empty) filter.IsEqualTo(entityID); else filter.IsNotEqualTo(Guid.Empty); var delFilter = new Filter(); delFilter.Expression = deletedExp; delFilter.IsEqualTo(Guid.Empty); filter.And(delFilter); return filter.Parent ?? filter; } public static Filter NotLinkValid(this Filter filter) { var mExp = filter.Expression as MemberExpression; if (mExp == null) throw new ArgumentException("Filter expression is not a MemberExpression"); filter.All(); var subFilter = new Filter(); var idExp = Expression.PropertyOrField(mExp, "ID"); var deletedExp = Expression.PropertyOrField(mExp, "Deleted"); subFilter.Expression = idExp; subFilter.IsEqualTo(Guid.Empty); var delFilter = new Filter(); delFilter.Expression = deletedExp; delFilter.IsNotEqualTo(Guid.Empty); subFilter.Or(delFilter); filter.And(subFilter); return filter.Parent ?? filter; } #region DeepClone Utility //public static object Clone(object from) //{ // if (from == null) // throw new ArgumentNullException("Source is null"); // if (into == null) // throw new ArgumentNullException("Target is null"); // return DoClone(from, null); //} /// /// Get the deep clone of an object. /// /// The type of the obj. /// It is the object used to deep clone. /// Return the deep clone. public static T Clone(T from, T into) where T : new() { if (from == null) throw new ArgumentNullException("Source is null"); return (T)DoClone(from, into); } private static FieldInfo[] GetFieldInfosIncludingBaseClasses(Type type, BindingFlags bindingFlags) { var fieldInfos = type.GetFields(bindingFlags); // If this class doesn't have a base, don't waste any time if (type.BaseType == null || type.BaseType == typeof(object)) return fieldInfos; // Otherwise, collect all types up to the furthest base class var currentType = type; var fieldComparer = new FieldInfoComparer(); var fieldInfoList = new HashSet(fieldInfos, fieldComparer); while (currentType != typeof(object)) { fieldInfos = currentType.GetFields(bindingFlags); fieldInfoList.UnionWith(fieldInfos); currentType = currentType.BaseType; } return fieldInfoList.ToArray(); } private class FieldInfoComparer : IEqualityComparer { public bool Equals(FieldInfo x, FieldInfo y) { return x.DeclaringType == y.DeclaringType && x.Name == y.Name; } public int GetHashCode(FieldInfo obj) { return obj.Name.GetHashCode() ^ obj.DeclaringType.GetHashCode(); } } /// /// The method implements deep clone using reflection. /// /// It is the object used to deep clone. /// Return the deep clone. private static object? DoClone(object from, object? into = null) { if (from == null) return null; if (from is MulticastDelegate) return null; var type = from.GetType(); // If the type of object is the value type, we will always get a new object when // the original object is assigned to another variable. So if the type of the // object is primitive or enum, we just return the object. We will process the // struct type subsequently because the struct type may contain the reference // fields. // If the string variables contain the same chars, they always refer to the same // string in the heap. So if the type of the object is string, we also return the // object. if (type.GetTypeInfo().IsPrimitive || type.GetTypeInfo().IsEnum || type == typeof(string)) return from; // If the type of the object is the Array, we use the CreateInstance method to get // a new instance of the array. We also process recursively this method in the // elements of the original array because the type of the element may be the reference // type. if (type.IsArray) { var basename = type.FullName.Replace("[]", ""); var typeElement = Type.GetType(basename); if (typeElement == null) typeElement = FindType(basename); var array = (from as Array)!; // not-null because type.IsArray var copiedArray = Array.CreateInstance(typeElement, array.Length); for (var i = 0; i < array.Length; i++) // Get the deep clone of the element in the original array and assign the // clone to the new array. copiedArray.SetValue(DoClone(array.GetValue(i)), i); return copiedArray; } // If the type of the object is class or struct, it may contain the reference fields, // so we use reflection and process recursively this method in the fields of the object // to get the deep clone of the object. // We use Type.IsValueType method here because there is no way to indicate directly whether // the Type is a struct type. if (type.GetTypeInfo().IsClass || type.GetTypeInfo().IsValueType) { var copiedObject = into; if (copiedObject == null) try { copiedObject = Activator.CreateInstance(from.GetType()); } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } if (copiedObject != null) { // Get all FieldInfo. var fields = GetFieldInfosIncludingBaseClasses(type.GetTypeInfo(), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); foreach (var field in fields.Where(x => !x.IsNotSerialized)) { var fieldValue = field.GetValue(from); if (fieldValue != null) // Get the deep clone of the field in the original object and assign the // clone to the field in the new object. field.SetValue(copiedObject, DoClone(fieldValue)); } } return copiedObject; } throw new ArgumentException("The object is unknown type"); } #endregion #region DeepCompare Utility public static bool CompareObjects(string prefix, object inputObjectA, object inputObjectB, string[] ignorePropertiesList, List> results) { var areObjectsEqual = true; //check if both objects are not null before starting comparing children if (inputObjectA != null && inputObjectB != null) { //create variables to store object values object value1, value2; var properties = inputObjectA.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); //get all public properties of the object using reflection foreach (var propertyInfo in properties) { var name = string.IsNullOrWhiteSpace(prefix) ? propertyInfo.Name : prefix + "." + propertyInfo.Name; //if it is not a readable property or if it is a ignorable property //ingore it and move on if (!propertyInfo.CanRead || ignorePropertiesList.Contains(name)) continue; //get the property values of both the objects value1 = propertyInfo.GetValue(inputObjectA, null); value2 = propertyInfo.GetValue(inputObjectB, null); // if the objects are primitive types such as (integer, string etc) //that implement IComparable, we can just directly try and compare the value if (IsAssignableFrom(propertyInfo.PropertyType) || IsPrimitiveType(propertyInfo.PropertyType) || IsValueType(propertyInfo.PropertyType)) { //compare the values if (!CompareValues(name, value1, value2, results)) { //Console.WriteLine("Property Name {0}", propertyInfo.Name); areObjectsEqual = false; } } //if the property is a collection (or something that implements IEnumerable) //we have to iterate through all items and compare values else if (IsEnumerableType(propertyInfo.PropertyType)) { //Console.WriteLine("Property Name {0}", propertyInfo.Name); if (!CompareEnumerations(name, value1, value2, ignorePropertiesList, results)) areObjectsEqual = false; } //if it is a class object, call the same function recursively again else if (propertyInfo.PropertyType.IsClass) { if (!CompareObjects(name, propertyInfo.GetValue(inputObjectA, null), propertyInfo.GetValue(inputObjectB, null), ignorePropertiesList, results)) areObjectsEqual = false; } else { areObjectsEqual = false; } } } else { areObjectsEqual = false; } return areObjectsEqual; } //true if c and the current Type represent the same type, or if the current Type is in the inheritance //hierarchy of c, or if the current Type is an interface that c implements, //or if c is a generic type parameter and the current Type represents one of the constraints of c. false if none of these conditions are true, or if c is null. private static bool IsAssignableFrom(Type type) { return typeof(IComparable).IsAssignableFrom(type); } private static bool IsPrimitiveType(Type type) { return type.IsPrimitive; } private static bool IsValueType(Type type) { return type.IsValueType; } private static bool IsEnumerableType(Type type) { return typeof(IEnumerable).IsAssignableFrom(type); } /// /// Compares two values and returns if they are the same. /// private static bool CompareValues(string name, object? value1, object? value2, List> results) { var areValuesEqual = true; var selfValueComparer = value1 as IComparable; // one of the values is null if ((value1 == null && value2 != null) || (value1 != null && value2 == null)) areValuesEqual = false; else if (selfValueComparer != null && selfValueComparer.CompareTo(value2) != 0) areValuesEqual = false; else if (!Equals(value1, value2)) areValuesEqual = false; if (!areValuesEqual) results.Add(new Tuple(name, value1, value2)); return areValuesEqual; } private static bool CompareEnumerations(string name, object value1, object value2, string[] ignorePropertiesList, List> results) { // if one of the values is null, no need to proceed return false; if (value1 == null && value2 != null) { results.Add(new Tuple(string.Format("{0}.Null", name), true, false)); return false; } if (value1 != null && value2 == null) { results.Add(new Tuple(string.Format("{0}.Null", name), false, true)); return false; } if (value1 != null && value2 != null) { IEnumerable enumValue1, enumValue2; enumValue1 = ((IEnumerable)value1).Cast(); enumValue2 = ((IEnumerable)value2).Cast(); // if the items count are different return false if (enumValue1.Count() != enumValue2.Count()) { results.Add(new Tuple(string.Format("{0}.Count", name), enumValue1.Count(), enumValue2.Count())); return false; } // if the count is same, compare individual item object enumValue1Item, enumValue2Item; Type enumValue1ItemType; for (var itemIndex = 0; itemIndex < enumValue1.Count(); itemIndex++) { enumValue1Item = enumValue1.ElementAt(itemIndex); enumValue2Item = enumValue2.ElementAt(itemIndex); enumValue1ItemType = enumValue1Item.GetType(); if (IsAssignableFrom(enumValue1ItemType) || IsPrimitiveType(enumValue1ItemType) || IsValueType(enumValue1ItemType)) { if (!CompareValues(string.Format("{0}[{1}]", name, itemIndex), enumValue1Item, enumValue2Item, results)) return false; } else if (!CompareObjects(name, enumValue1Item, enumValue2Item, ignorePropertiesList, results)) { return false; } } } return true; } #endregion #region Expression Serialisation public static string MemberExpressionToString(MemberExpression mExpression) { var mOriginal = mExpression; var path = ""; while (true) { if (mExpression.Expression.NodeType == ExpressionType.MemberAccess) { var propInfo = mExpression.Expression .GetType().GetTypeInfo().GetProperty("Member"); var propValue = (propInfo.GetValue(mExpression.Expression, null) as PropertyInfo)!; path = propValue.Name + "." + path; mExpression = (mExpression.Expression as MemberExpression)!; } else if (mExpression.Expression.NodeType == ExpressionType.Convert) { mExpression = MemberExpression.PropertyOrField((mExpression.Expression as UnaryExpression)?.Operand as MemberExpression, mExpression.Member.Name); } else { break; } } return path + mOriginal.Member.Name; } private enum SerialisedExpressionType { Member, Index, Parameter } public static void SerialiseExpression(this CoreBinaryWriter writer, Type t, Expression expression, bool includeType) { if (includeType) { writer.Write(t.EntityName()); } if (expression.NodeType == ExpressionType.MemberAccess) { writer.Write((byte)SerialisedExpressionType.Member); writer.Write(MemberExpressionToString((expression as MemberExpression)!)); } else if (expression.NodeType == ExpressionType.Index) { writer.Write((byte)SerialisedExpressionType.Index); var iexp = (IndexExpression)expression; var sexp = expression.ToString().Split('['); writer.Write(sexp[0]); // Member writer.Write(sexp[1].Replace("]", "").Replace("\"", "")); // Key } else { writer.Write((byte)SerialisedExpressionType.Parameter); } } public static Expression DeserialiseExpression(this CoreBinaryReader reader, Type t) { var expressionType = (SerialisedExpressionType)reader.ReadByte(); switch (expressionType) { case SerialisedExpressionType.Member: var member = reader.ReadString(); return CreateMemberExpression(t, member); case SerialisedExpressionType.Index: member = reader.ReadString(); var key = reader.ReadString(); return CreateIndexExpression(t, member, key); case SerialisedExpressionType.Parameter: default: return Expression.Parameter(t, "x"); } } /// /// Deserialise the expression, with type information included in the serialised form. /// /// /// public static Expression DeserialiseExpression(this CoreBinaryReader reader) { var entityName = reader.ReadString(); var t = GetEntity(entityName); return reader.DeserialiseExpression(t); } public static string ExpressionToString(Expression> expression) => ExpressionToString(typeof(T), expression); public static string ExpressionToString(Type t, Expression expression) { if (expression.NodeType == ExpressionType.MemberAccess) { var mexp = (MemberExpression)expression; var morg = (MemberExpression)expression; var Path = ""; while (mexp.Expression.NodeType == ExpressionType.MemberAccess) { var propInfo = mexp.Expression .GetType().GetTypeInfo().GetProperty("Member"); var propValue = propInfo.GetValue(mexp.Expression, null) as PropertyInfo; Path = propValue.Name + "." + Path; mexp = mexp.Expression as MemberExpression; } return Path + morg.Member.Name; } else if (expression.NodeType == ExpressionType.Index) { var iexp = (IndexExpression)expression; var sexp = expression.ToString().Split('['); return sexp[0]; } return expression.ToString(); } public static string ExpressionToString(Type t, Expression expression, bool includetype) { var result = new Dictionary(); if (expression.NodeType == ExpressionType.MemberAccess) { var mexp = (expression as MemberExpression)!; result["Member"] = MemberExpressionToString(mexp); } else if (expression.NodeType == ExpressionType.Index) { var iexp = (IndexExpression)expression; var sexp = expression.ToString().Split('['); result["Member"] = sexp[0]; result["Key"] = sexp[1].Replace("]", "").Replace("\"", ""); } result["Type"] = t.EntityName(); return JsonConvert.SerializeObject(result); //ExpressionSerializer ser = new ExpressionSerializer(new Serialize.Linq.Serializers.JsonSerializer(),new Serialize.Linq.Factories.FactorySettings() { UseRelaxedTypeNames=true } ); //String result = ser.SerializeText(expression); //return result; //if (expression.NodeType == ExpressionType.MemberAccess) //{ // MemberExpression memberExpression = expression as MemberExpression; // MemberExpression memberExpressionOrg = memberExpression as MemberExpression; // string Path = ""; // while (memberExpression.Expression.NodeType == ExpressionType.MemberAccess) // { // var propInfo = memberExpression.Expression // .GetType().GetTypeInfo().GetProperty("Member"); // var propValue = propInfo.GetValue(memberExpression.Expression, null) // as PropertyInfo; // Path = propValue.Name + "." + Path; // memberExpression = memberExpression.Expression as MemberExpression; // } // if (includetype) // return t.FullName + " => " + Path + memberExpressionOrg.Member.Name; // else // return Path + memberExpressionOrg.Member.Name; //} //else if (expression.NodeType == ExpressionType.Index) //{ // if (includetype) // return t.FullName + " => " + expression.ToString(); // else // return expression.ToString(); //} //else //{ // if (includetype) // return t.FullName + " => " + expression.ToString(); // else // return expression.ToString(); //} } public static Expression StringToExpression(string expression) { var comps = JsonConvert.DeserializeObject>(expression); var t = GetEntity(comps["Type"]); if (comps.TryGetValue("Key", out var key)) return CreateIndexExpression(t, comps["Member"], key); else if (comps.TryGetValue("Member", out var member)) return CreateMemberExpression(t, member); else return Expression.Parameter(t, "x"); //ExpressionSerializer ser = new ExpressionSerializer(new Serialize.Linq.Serializers.JsonSerializer(), new Serialize.Linq.Factories.FactorySettings() { UseRelaxedTypeNames = true }); //Expression result = ser.DeserializeText(expression); //return result; //String[] comps = expression.Split(new String[] { " => " }, StringSplitOptions.None); //Type type = comps.Length > 1 ? CoreUtils.GetEntity(comps[0]) : t; //return CreateExpression(type, comps[comps.Length - 1]); } public static Expression> CreateLambdaExpression(string column) { var parameter = Expression.Parameter(typeof(T), "x"); var property = CreateMemberExpression(typeof(T), column); var converted = Expression.Convert(property, typeof(object)); //var property = Expression.Convert(Expression.Property(parameter, column),typeof(object)); return Expression.Lambda>(converted, parameter); } public static Expression> CreateLambdaExpression(string column) { var parameter = Expression.Parameter(typeof(T), "x"); var property = CreateMemberExpression(typeof(T), column); var result = Expression.Lambda>(property, parameter); return result; } public static Expression ConvertExpression(Type source, Expression lambda, Type target, Expression anchor) { var parent = ExpressionToString(target, anchor, false); var child = ExpressionToString(source, lambda, false); var property = string.Join(".", parent, child); return CreateMemberExpression(target, property); } public static Expression CreateMemberExpression(Type type, string propertyName) { var param = Expression.Parameter(type, "x"); Expression body = param; foreach (var member in propertyName.Split('.')) body = Expression.PropertyOrField(body, member); return (MemberExpression)body; } public static IndexExpression CreateIndexExpression(Type type, string propertyname, string key) { var param = Expression.Parameter(type, "x"); var propmod = propertyname.Replace("x.", "").Replace(".Item", ""); var bar = Expression.Property(param, propmod); var dictionaryType = type.GetProperty(propmod).PropertyType; var indexerProp = dictionaryType.GetProperty("Item"); var dictKeyConstant = Expression.Constant(key); var dictAccess = Expression.MakeIndex(bar, indexerProp, new[] { dictKeyConstant }); return dictAccess; //var propertyType = indexerProp.PropertyType; //var right = Expression.Constant(Convert.ChangeType("newValue", propertyType)); //var expression = Expression.MakeBinary(ExpressionType.Equal, dictAccess, right); } public static MemberExpression ExtractMemberExpression(Expression> expression) { if(!TryFindMemberExpression(expression.Body, out var me)) { throw new Exception("Expression is not a member expression!"); } return me; } public static MemberExpression ExtractMemberExpression(Expression> expression) { TryFindMemberExpression(expression.Body, out var me); return me; } public static MemberExpression ExtractMemberExpression(Expression expression) { TryFindMemberExpression(Expression.Lambda(expression).Body, out var me); return me; } #endregion public static string FormatException(Exception err) { var messages = new List(); var e2 = err; while (e2 != null) { messages.InsertRange(0, new[] { e2.Message, e2.StackTrace, "============================================" }); e2 = e2.InnerException; } return string.Join("\n", messages); } public static void LogException(string userid, Exception err, string? extra = null) { Logger.Send(LogType.Error, userid, (extra != null ? $"{extra}: " : "") + CoreUtils.FormatException(err)); } #region OneToMany Relationships public static Type? GetOneToMany(Type TMany, Type TOne) { var o2m = TMany.GetInterfaces().FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IOneToMany<>) && i.GenericTypeArguments.Contains(TOne) ); return o2m; } public static PropertyInfo GetOneToManyProperty(Type TMany, Type TOne) { return TMany.GetProperties().FirstOrDefault(x => x.PropertyType.GetInterfaces().Contains(typeof(IEntityLink)) && x.PropertyType.GetInheritedGenericTypeArguments().FirstOrDefault() == TOne && !Attribute.IsDefined(x, typeof(ObsoleteAttribute)) ); } #endregion #region ManyToMany Relationships public static Type? GetManyToMany(Type TManyToMany, Type TThis) { var m2m = TManyToMany.GetInterfaces().FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IManyToMany<,>) && i.GenericTypeArguments.Contains(TThis) ); return m2m; } public static Type GetManyToManyOtherType(Type TManyToMany, Type TThis) { var m2m = TManyToMany.GetInterfaces().FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IManyToMany<,>) && i.GenericTypeArguments.Contains(TThis) ); return m2m.GenericTypeArguments.FirstOrDefault(x => x != TThis); } public static PropertyInfo GetManyToManyThisProperty(Type TManyToMany, Type TThis) { return TManyToMany.GetProperties().FirstOrDefault(x => x.PropertyType.GetInterfaces().Contains(typeof(IEntityLink)) && x.PropertyType.GetInheritedGenericTypeArguments().FirstOrDefault() == TThis && !Attribute.IsDefined(x, typeof(ObsoleteAttribute)) ); } public static PropertyInfo GetManyToManyOtherProperty(Type TManyToMany, Type TThis) { var otherType = GetManyToManyOtherType(TManyToMany, TThis); return TManyToMany.GetProperties().FirstOrDefault(x => x.PropertyType.GetInterfaces().Contains(typeof(IEntityLink)) && x.PropertyType.GetInheritedGenericTypeArguments().FirstOrDefault() == otherType && !Attribute.IsDefined(x, typeof(ObsoleteAttribute)) ); } #endregion /// /// Get a dictionary of values representing the current state of an entity /// /// The Entity being scanned /// The list of properties to scan, or null for all /// A dictionary containing the properties and values for the entity public static Dictionary GetValues(BaseObject item, IEnumerable? props = null) { var result = new Dictionary(); props ??= DatabaseSchema.Properties(item.GetType());//.Where(x => !(x.Editor is NullEditor)); foreach (var prop in props) if (prop is CustomProperty) try { result[prop.Name] = item.UserProperties[prop.Name]; } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } else try { object? value; var getter = prop.Getter(); if (getter != null) value = getter.Invoke(item); else value = CoreUtils.GetPropertyValue(item, prop.Name); result[prop.Name] = value; } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } return result; } /// /// Given a certain entity, merge the changes with the current state of the entity into an updated list /// /// Th Entity being updated /// The previous set of changes to this entity /// The list of properties to scan, or null for all /// The resulting updated list of changes public static void MergeChanges(Dictionary previous, Dictionary current, Dictionary changes) { foreach (var (key, prevVal) in previous) if (prevVal is null) { if (current[key] != null) changes[key] = current[key]; } else { if (!current.TryGetValue(key, out var curVal) || curVal is null) changes[key] = null; else if (!object.Equals(previous[key], curVal)) changes[key] = curVal; } } public static Dictionary MonitorChanges(T item, Action action, Dictionary? changes = null, IEnumerable? props = null) where T : BaseObject { var result = changes == null ? new Dictionary() : changes; var previous = GetValues(item, props); action.Invoke(); var current = GetValues(item, props); MergeChanges(previous, current, result); return result; } public static DateTime StartOfWeek(this DateTime dt, DayOfWeek startOfWeek) { int diff = (7 + (dt.DayOfWeek - startOfWeek)) % 7; return dt.AddDays(-1 * diff).Date; } #region String Utilities /// /// Check if . /// If it is empty, returns , otherwise, returns . /// /// /// Basically the null-coalescing operator ?? for strings. /// /// /// /// [return: NotNullIfNotNull("def")] public static string? NotWhiteSpaceOr(this string? s, string? def = null) { if (string.IsNullOrWhiteSpace(s)) { return def; } return s; } /// /// Extension method version of . /// /// The string to check /// true if is or composed only of whitespace. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? s) { return string.IsNullOrWhiteSpace(s); } public static bool IsBase64String(this string s) { s = s.Trim(); return s.Length % 4 == 0 && Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None); } public static string CalculateCRC(byte[] data) { var hash = Crc32.Compute(data); return string.Format("{0:X8}", hash); } public static string StripHTML(this string? html) { if (string.IsNullOrWhiteSpace(html)) return ""; var result = StripHtmlStyles.Replace(html, ""); result = StripHtmlTags.Replace(result, ""); result = StripSpecialCharacters.Replace(result, ""); //result = StripQuotes.Replace(result, "\""); result = StripLineBreaks.Replace(result, "$1"); result = result.Replace("\0", "").Trim(); return result; } private static Regex NeatifyRegex = new Regex(@" (?<=[A-Z])(?=[A-Z][a-z]) | (?<=[^A-Z])(?=[A-Z]) | (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace); public static string Neatify(string caption) { if (string.Equals(caption, "ID")) return caption; var result = NeatifyRegex.Replace(caption.Replace(".", ""), " "); while (result.Contains(" ")) result = result.Replace(" ", " "); if (result.EndsWith(" ID")) result = string.Join(" ", result.Split(' ').Reverse().Skip(1).Reverse()); return result.Trim(); } public static string Codify(string? text) { char[] numbers = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; if (string.IsNullOrWhiteSpace(text)) return "??"; var result = ""; var comps = text.ToUpper().Split(' '); foreach (var comp in comps) { List chars = new List(); foreach (char c in comp.ToCharArray()) { chars.Add(c); if (!numbers.Contains(c)) break; } var comb = new String(chars.ToArray()); result += comb; } return string.IsNullOrWhiteSpace(result) ? "??" : result; } public static string Truncate(this string value, int maxLength) { if(value.Length > maxLength) { return value[..maxLength]; } return value; } #endregion #region IEnumerable Utilities /// /// Returns as a ; /// if it is already a , it is directly returned, instead of copying. /// public static List AsList(this IEnumerable enumerable) { if(enumerable is List list) { return list; } return enumerable.ToList(); } /// /// Returns as an ; /// if it is already a , it is directly returned, instead of copying. /// public static IList AsIList(this IEnumerable enumerable) { if (enumerable is IList list) { return list; } return enumerable.ToList(); } /// /// Returns as an array; /// if it is already an array, it is directly returned, instead of copying. /// public static T[] AsArray(this IEnumerable enumerable) { if (enumerable is T[] arr) { return arr; } return enumerable.ToArray(); } public static U[] ToArray(this IList from, Func mapFunc) { var to = new U[from.Count]; for(int i = 0; i < from.Count; ++i) { to[i] = mapFunc(from[i]); } return to; } /// /// Concatenate with , returning a new array. /// /// /// /// /// public static T[] Concatenate(this T[] arr1, T[] arr2) { var newArr = new T[arr1.Length + arr2.Length]; arr1.CopyTo(newArr, 0); arr2.CopyTo(newArr, arr1.Length); return newArr; } /// /// Concatenate all together. /// /// /// /// public static T[] Concatenate(params T[][] arrays) { var newArr = new T[arrays.Sum(x => x.Length)]; for(int i = 0, idx = 0; i < arrays.Length; ++i) { var arr = arrays[i]; arr.CopyTo(newArr, idx); idx += arr.Length; } return newArr; } public static IEnumerable AnyOr(this IEnumerable enumerable, IEnumerable or) { var any = false; foreach(var item in enumerable) { any = true; yield return item; } if (!any) { foreach(var item in or) { yield return item; } } } public static IEnumerable One(T item) { yield return item; } public static IEnumerable One(Func fnc) { yield return fnc(); } public static (List, List) PartitionToList(this IEnumerable enumerable, Func predicate) { var trueResult = new List(); var falseResult = new List(); foreach(var item in enumerable) { if (predicate(item)) { trueResult.Add(item); } else { falseResult.Add(item); } } return (trueResult, falseResult); } /// /// Get the value in for the given , adding a new instance of /// if the value was not found. /// /// /// /// /// /// public static TValue GetValueOrAdd(this IDictionary dictionary, TKey key) where TValue : new() { if(!dictionary.TryGetValue(key, out var value)) { value = new TValue(); dictionary.Add(key, value); } return value; } /// /// Add a range of values to a dictionary, using the method. /// /// /// Throws an for duplicate keys. /// /// /// /// /// /// /// public static TDict AddRange(this TDict dictionary, IEnumerable> items) where TDict : IDictionary { foreach(var item in items) { dictionary.Add(item.Key, item.Value); } return dictionary; } public static IEnumerable NotNull(this IEnumerable enumerable) where T : struct { foreach(var obj in enumerable) { if (obj.HasValue) { yield return obj.Value; } } } public static IEnumerable NotNull(this IEnumerable enumerable) where T : class { foreach (var obj in enumerable) { if (obj != null) { yield return obj; } } } public static IEnumerable NotNull(this IEnumerable enumerable, Func f) where TValue : struct { foreach (var obj in enumerable) { var v = f(obj); if (v.HasValue) { yield return v.Value; } } } public static IEnumerable NotNull(this IEnumerable enumerable, Func f) where TValue : class { foreach (var obj in enumerable) { var v = f(obj); if (v != null) { yield return v; } } } public static IEnumerable> WithIndex(this IEnumerable enumerable) { int i = 0; foreach(var obj in enumerable) { yield return new KeyValuePair(i, obj); ++i; } } #endregion } }