// Copyright (c) 2023-present schick Informatik // Description: Container class for self-learning, volatile information that // is added by parsing Excel sheets. Here common wrong/misspelled data fields // are mapped to valid ones. using System; using System.Collections.Generic; using System.ComponentModel; using System.Data.SqlClient; using System.Threading.Tasks; namespace bsmd.database { public class ValueMapping : DatabaseEntityAsync, INotifyPropertyChanged { #region Construction public ValueMapping() { this.tablename = "[dbo].[ValueMapping]"; } #endregion #region enums public enum MappingType { COUNTRY, GENDER, DOCUMENT_TYPE, LOCODE } #endregion #region Fields private static Dictionary> _dicts; private static Dictionary> _invalidKeys; private string _key, _value, _valueText; #endregion #region Properties public event PropertyChangedEventHandler PropertyChanged; public static Dictionary> Dicts { get { if (_dicts == null) { _dicts = new Dictionary>(); foreach (MappingType type in Enum.GetValues(typeof(MappingType))) _dicts[type] = new Dictionary(); } return _dicts; } } public static Dictionary> InvalidKeys { get { if (_invalidKeys == null) { _invalidKeys = new Dictionary>(); // get the keys initialized that cannot be assigned foreach (MappingType type in Enum.GetValues(typeof(MappingType))) _invalidKeys[type] = new List(); } return _invalidKeys; } } public MappingType EntryType { get; private set; } public string Key { get { return _key; } set { if (!value.Equals(_key)) this.IsDirty = true; this._key = value; this.OnPropertyChanged("Key"); } } public string Value { get { return _value; } set { if (!value.Equals(_value)) this.IsDirty = true; _value = value; OnPropertyChanged("Value"); } } public string ValueText { get { return _valueText; } set { this._valueText = value; this.OnPropertyChanged("ValueText"); } } public DateTime? Created { get; private set; } public DateTime? Changed { get; private set; } #endregion #region public funcs /// /// creates and saves a new entry and adds it to the internal dictionaries /// /// true if entry was actually created, false if already present public static async Task Create(MappingType type, string key, string value) { if (!Dicts[type].ContainsKey(key)) { ValueMapping vm = new ValueMapping() { EntryType = type, Key = key, Value = value }; if (value.Equals("*")) InvalidKeys[type].Add(key); else Dicts[type][key] = vm; return await DBManagerAsync.SaveAsync(vm) == 1; } return false; } /// /// Create through manual editing in global scope /// public static ValueMapping Create(MappingType type) { ValueMapping vm = new ValueMapping() { EntryType = type }; return vm; } /// /// deletes an entry and removes it from the database /// /// true if successful, false if value was not found public static async Task Delete(MappingType type, string key) { if (!Dicts.ContainsKey(type)) return false; if (!Dicts[type].ContainsKey(key)) return false; ValueMapping vm = Dicts[type][key]; Dicts[type].Remove(key); return await DBManagerAsync.DeleteAsync(vm) == 1; } /// /// updates an existing value and saves it /// /// true if successful public async Task Update(string newValue) { this.Value = newValue; return await DBManagerAsync.SaveAsync(this) == 1; } /// /// (re-)loads all value mapping dictionaries /// public static async void LoadDicts() { foreach(MappingType type in Enum.GetValues(typeof(MappingType))) { Dicts[type].Clear(); List mappings = await DBManagerAsync.LoadValuesForType(type); foreach (ValueMapping mapping in mappings) { if (mapping.Value.Equals("*")) InvalidKeys[type].Add(mapping.Key); else Dicts[type][mapping.Key] = mapping; } } } #endregion #region DatabaseEntity implementation public override void PrepareSave(System.Data.IDbCommand cmd) { SqlCommand scmd = cmd as SqlCommand; scmd.Parameters.AddWithValue("@TYPE", this.EntryType); scmd.Parameters.AddWithNullableValue("@KEY", this.Key); scmd.Parameters.AddWithNullableValue("@VALUE", this.Value); if(this.IsNew) { this.CreateId(); scmd.Parameters.AddWithValue("@ID", this.Id); scmd.CommandText = $"INSERT INTO {Tablename} (Id, EntryType, MappingKey, MappingValue) VALUES (@ID, @TYPE, @KEY, @VALUE)"; } else { scmd.Parameters.AddWithValue("@ID", this.Id); scmd.CommandText = $"UPDATE {Tablename} SET EntryType = @TYPE, MappingKey = @KEY, MappingValue = @VALUE WHERE Id = @ID"; } } public override void PrepareLoadCommand(System.Data.IDbCommand cmd, Message.LoadFilter filter, params object[] criteria) { string query = $"SELECT Id, EntryType, MappingKey, MappingValue, Created, Changed FROM {Tablename}"; switch (filter) { case Message.LoadFilter.BY_TYPE: query += " WHERE EntryType = @TYPE"; ((SqlCommand)cmd).Parameters.AddWithValue("@TYPE", criteria[0]); break; default: break; } query += " ORDER BY MappingKey"; cmd.CommandText = query; } protected override DatabaseEntityAsync ReadRowFromReader(System.Data.IDataReader reader) { ValueMapping vm = null; if (reader != null) { vm = new ValueMapping(); vm.id = reader.GetGuid(0); if (!reader.IsDBNull(1)) vm.EntryType = (ValueMapping.MappingType)reader.GetByte(1); if (!reader.IsDBNull(2)) vm._key = reader.GetString(2); if (!reader.IsDBNull(3)) vm._value = reader.GetString(3); if (!reader.IsDBNull(4)) vm.Created = reader.GetDateTime(4); if (!reader.IsDBNull(5)) vm.Changed = reader.GetDateTime(5); } return vm; } #endregion #region protected methods protected void OnPropertyChanged(string name = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } #endregion } }