123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Data;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Net.Http.Headers;
- using System.Reflection;
- namespace InABox.Core
- {
- [Serializable]
- public class CoreTable : ICoreTable, ISerializeBinary //: IEnumerable, INotifyCollectionChanged
- {
- #region Fields
- private List<CoreRow>? rows;
- private List<CoreColumn> columns = new List<CoreColumn>();
- private string tableName;
- #endregion
- #region Properties
- public string TableName { get => tableName; }
-
- public int TotalRecords { get; set; }
- public int Offset { get; set; } = 0;
- public IList<CoreColumn> Columns { get => columns; }
- public IList<CoreRow> Rows
- {
- get
- {
- rows ??= new List<CoreRow>();
- //this.rows.CollectionChanged += OnRowsCollectionChanged;
- return rows;
- }
- }
- [field: NonSerialized]
- public IList<Func<object, object?>?> Getters { get; } = new List<Func<object, object?>?>();
- [field: NonSerialized]
- public Dictionary<string, IList<Action<object, object>?>> Setters { get; } = new Dictionary<string, IList<Action<object, object>?>>();
- #endregion
- public CoreTable() : this("")
- {
- }
- public CoreTable(string tableName): base()
- {
- this.tableName = tableName;
- }
- public CoreTable(Type type) : this()
- {
- LoadColumns(type);
- }
-
- public void AddColumn<T>(Expression<Func<T, object>> column)
- {
- Columns.Add(
- new CoreColumn()
- {
- ColumnName = CoreUtils.GetFullPropertyName(column, "."),
- DataType = column.ReturnType
- }
- );
- }
-
- public CoreRow NewRow(bool populate = false)
- {
- var result = new CoreRow(this);
- if (populate)
- foreach (var column in Columns)
- result[column.ColumnName] = column.DataType.GetDefault();
- return result;
- }
- public void LoadColumns(Type T)
- {
- var iprops = DatabaseSchema.Properties(T);
- foreach (var iprop in iprops)
- Columns.Add(new CoreColumn { ColumnName = iprop.Name, DataType = iprop.PropertyType });
- }
-
- public void LoadColumns(IColumns columns)
- {
- foreach (var col in columns)
- Columns.Add(new CoreColumn { ColumnName = col.Property, DataType = col.Type });
- }
-
- public void LoadColumns(IEnumerable<CoreColumn> columns)
- {
- Columns.Clear();
- foreach (var col in columns)
- Columns.Add(new CoreColumn() { ColumnName = col.ColumnName, DataType = col.DataType });
- }
- /// <summary>
- /// Add the rows from the given <paramref name="table"/> to this table.
- /// </summary>
- /// <remarks>
- /// This method assumes that the columns of this table and <paramref name="table"/> are the same; it is intended for paging functionality.
- /// </remarks>
- /// <param name="table"></param>
- public void AddPage(CoreTable table)
- {
- foreach(var row in table.Rows)
- {
- var newRow = NewRow(populate: false);
- newRow.LoadValues(row.Values);
- Rows.Add(newRow);
- }
- }
- public void LoadRows(IEnumerable<object> objects)
- {
- foreach (var obj in objects)
- {
- LoadRow(obj);
- }
- }
- public void LoadRows(IEnumerable<CoreRow> rows)
- {
- foreach (var row in rows)
- {
- var newrow = NewRow();
- FillRow(newrow, row);
- Rows.Add(newrow);
- }
- }
- public void LoadFrom<T1, T2>(CoreTable table, CoreFieldMap<T1, T2> mappings, Action<CoreRow>? customization = null)
- {
- foreach (var row in table.Rows)
- {
- var newrow = NewRow();
- foreach (var map in mappings.Fields)
- newrow.Set(map.To, row.Get(map.From));
- customization?.Invoke(newrow);
- Rows.Add(newrow);
- }
- }
- public void FillRow(CoreRow row, object obj)
- {
- var bFirst = Getters.Count == 0;
- for(var i = 0; i < Columns.Count; ++i)
- {
- try
- {
- if (bFirst)
- {
- var prop = DatabaseSchema.Property(obj.GetType(), Columns[i].ColumnName);
- Getters.Add(prop?.Getter());
- }
- var getter = Getters[i];
- if (getter != null)
- row.Set(i, getter(obj));
- else
- row.Set(i, CoreUtils.GetPropertyValue(obj, Columns[i].ColumnName));
- }
- catch (Exception e)
- {
- Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
- }
- }
- }
- public void FillRow(CoreRow row, CoreRow from)
- {
- foreach (var col in Columns)
- try
- {
- if (from.Table.Columns.Any(x => x.ColumnName.Equals(col.ColumnName)))
- row[col.ColumnName] = from[col.ColumnName];
- }
- catch (Exception e)
- {
- Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
- }
- }
- public void Filter(Func<CoreRow, bool> predicate)
- {
- for (var i = Rows.Count - 1; i > -1; i--)
- {
- var row = Rows[i];
- var predresult = predicate.Invoke(row);
- if (!predresult)
- Rows.Remove(row);
- }
- }
-
- #region ToDictionary
- public IDictionary ToDictionary(string keycol, string displaycol, string sortcol = "")
- {
- var kc = Columns.FirstOrDefault(x => x.ColumnName.Equals(keycol));
- var dc = Columns.FirstOrDefault(x => x.ColumnName.Equals(displaycol));
- var dt = typeof(Dictionary<,>).MakeGenericType(kc.DataType, dc.DataType);
- var id = (Activator.CreateInstance(dt) as IDictionary)!;
- var sorted = string.IsNullOrWhiteSpace(sortcol) ? Rows : Rows.OrderBy(x => x.Get<object>(sortcol)).ToList();
- foreach (var row in sorted)
- id[row[keycol]] = row[displaycol];
- return id;
- }
- public Dictionary<TKey, TValue> ToDictionary<T, TKey, TValue>(Expression<Func<T, TKey>> key, Expression<Func<T, TValue>> value,
- Expression<Func<T, object>>? sort = null)
- {
- var result = new Dictionary<TKey, TValue>();
- var sorted = sort == null ? Rows : Rows.OrderBy(x => x.Get(sort)).ToList();
- foreach (var row in Rows)
- result[row.Get(key)] = row.Get(value);
- return result;
- }
- public Dictionary<TKey, string> ToDictionary<T, TKey>(Expression<Func<T, TKey>> key, Expression<Func<T, object>>[] values,
- Expression<Func<T, object>>? sort = null)
- {
- var result = new Dictionary<TKey, string>();
- var sorted = sort == null ? Rows : Rows.OrderBy(x => x.Get(sort)).ToList();
- foreach (var row in Rows)
- {
- var display = new List<object>();
- foreach (var value in values)
- display.Add(row.Get(value));
- result[row.Get(key)] = string.Join(" : ", display);
- }
- return result;
- }
- public Dictionary<TKey, TValue> ToDictionary<T, TKey, TValue>(Expression<Func<T, TKey>> key, Func<CoreRow, TValue> value,
- Expression<Func<T, object>>? sort = null)
- {
- var result = new Dictionary<TKey, TValue>();
- var sorted = sort == null ? Rows : Rows.OrderBy(x => x.Get(sort)).ToList();
- foreach (var row in Rows)
- result[row.Get(key)] = value(row);
- return result;
- }
- public void LoadDictionary<T, TKey, TValue>(Dictionary<TKey, TValue> dictionary, Expression<Func<T, TKey>> key,
- Expression<Func<T, TValue>> value)
- {
- foreach (var row in Rows)
- dictionary[row.Get(key)] = row.Get(value);
- }
-
- public Dictionary<TKey, string> IntoDictionary<T, TKey>(Dictionary<TKey, string> result, Expression<Func<T, TKey>> key,
- params Expression<Func<T, object>>[] values)
- {
- foreach (var row in Rows)
- {
- var display = new List<object>();
- foreach (var value in values)
- display.Add(row.Get(value));
- result[row.Get(key)] = string.Join(" : ", display);
- }
- return result;
- }
- public Dictionary<TKey, TValue> IntoDictionary<T, TKey, TValue>(Dictionary<TKey, TValue> result, Expression<Func<T, TKey>> key,
- Func<CoreRow, TValue> value)
- {
- foreach (var row in Rows)
- result[row.Get(key)] = value(row);
- return result;
- }
-
- #endregion
- public DataTable ToDataTable(string name = "", IColumns? additionalColumns = null)
- {
- var result = new DataTable(name);
- foreach (var column in Columns)
- result.Columns.Add(column.ColumnName.Replace('.', '_'), column.DataType);
- if(additionalColumns != null)
- {
- foreach (var column in additionalColumns)
- result.Columns.Add(column.Property.Replace('.', '_'), column.Type);
- }
- //result.Columns["ID"].Unique = true;
- //result.PrimaryKey = new DataColumn[] { result.Columns["ID"] };
- foreach (var row in Rows)
- {
- //result.Rows.Add(row.Values.ToArray());
- var newrow = result.NewRow();
- newrow.ItemArray = row.Values.ToArray();
- result.Rows.Add(newrow);
- }
- return result;
- }
-
- #region ToObjects
- public IEnumerable<BaseObject> ToObjects(Type T)
- => Rows.Select(x => x.ToObject(T));
- public IEnumerable<T> ToObjects<T>() where T : BaseObject, new()
- => Rows.Select(x => x.ToObject<T>());
- public List<T> ToList<T>() where T : BaseObject, new()
- {
- var list = new List<T>(Rows.Count);
- for(int i = 0; i < Rows.Count; ++i)
- {
- list.Add(Rows[i].ToObject<T>());
- }
- return list;
- }
- public T[] ToArray<T>() where T : BaseObject, new()
- {
- var arr = new T[Rows.Count];
- for(int i = 0; i < Rows.Count; ++i)
- {
- arr[i] = Rows[i].ToObject<T>();
- }
- return arr;
- }
- #endregion
- #region ToTuples
- public IEnumerable<Tuple<T1, T2>> ToTuples<TType, T1, T2>(
- Expression<Func<TType, T1>> item1,
- Expression<Func<TType, T2>> item2
- )
- {
- return Rows.Select(row =>
- new Tuple<T1, T2>
- (
- row.Get<TType,T1>(item1),
- row.Get<TType,T2>(item2)
- )
- );
- }
-
- public IEnumerable<Tuple<T1, T2, T3>> ToTuples<TType, T1, T2, T3>(
- Expression<Func<TType, T1>> item1,
- Expression<Func<TType, T2>> item2,
- Expression<Func<TType, T3>> item3)
- {
- return Rows.Select(row =>
- new Tuple<T1, T2, T3>
- (
- row.Get<TType,T1>(item1),
- row.Get<TType,T2>(item2),
- row.Get<TType,T3>(item3)
- )
- );
- }
-
- public IEnumerable<Tuple<T1, T2, T3, T4>> ToTuples<TType, T1, T2, T3, T4>(
- Expression<Func<TType, T1>> item1,
- Expression<Func<TType, T2>> item2,
- Expression<Func<TType, T3>> item3,
- Expression<Func<TType, T4>> item4
- )
- {
- return Rows.Select(row =>
- new Tuple<T1, T2, T3, T4>
- (
- row.Get<TType,T1>(item1),
- row.Get<TType,T2>(item2),
- row.Get<TType,T3>(item3),
- row.Get<TType,T4>(item4)
- )
- );
- }
-
- public IEnumerable<Tuple<T1, T2, T3, T4, T5>> ToTuples<TType, T1, T2, T3, T4, T5>(
- Expression<Func<TType, T1>> item1,
- Expression<Func<TType, T2>> item2,
- Expression<Func<TType, T3>> item3,
- Expression<Func<TType, T4>> item4,
- Expression<Func<TType, T5>> item5
- )
- {
- return Rows.Select(row =>
- new Tuple<T1, T2, T3, T4,T5>
- (
- row.Get<TType,T1>(item1),
- row.Get<TType,T2>(item2),
- row.Get<TType,T3>(item3),
- row.Get<TType,T4>(item4),
- row.Get<TType,T5>(item5)
- )
- );
- }
-
- #endregion
- public void CopyTo(DataTable table)
- {
- var columns = new List<string>();
- foreach (var column in Columns)
- columns.Add(column.ColumnName.Replace('.', '_'));
- foreach (var row in Rows)
- {
- var newrow = table.NewRow();
- for (var i = 0; i < columns.Count; i++)
- newrow[columns[i]] = row.Values[i];
- table.Rows.Add(newrow);
- }
- }
- public void CopyTo(CoreTable table)
- {
- var columns = new List<string>();
- //foreach (var column in Columns)
- // columns.Add(column.ColumnName.Replace('.', '_'));
- foreach (var row in Rows)
- {
- var newrow = table.NewRow();
- foreach (var column in Columns)
- if (table.Columns.Any(x => x.ColumnName.Equals(column.ColumnName)))
- newrow[column.ColumnName] = row[column.ColumnName];
- //for (int i = 0; i < columns.Count; i++)
- // newrow.Set(columns[i], row.Values[i]);
- table.Rows.Add(newrow);
- }
- }
- #region Extract Values
-
- public TValue[] ExtractValues<TSource, TValue>(Expression<Func<TSource, TValue>> column, bool distinct = true)
- {
- var columnIdx = GetColumnIndex(column);
- if (distinct)
- {
- return Rows.Select(r => r.Get<TValue>(columnIdx)).Distinct().ToArray();
- }
- else
- {
- return Rows.ToArray(r => r.Get<TValue>(columnIdx));
- }
- }
- public TValue[] ExtractValues<TValue>(string column, bool distinct = true)
- {
- var columnIdx = GetColumnIndex(column);
- if (distinct)
- {
- return Rows.Select(r => r.Get<TValue>(columnIdx)).Distinct().ToArray();
- }
- else
- {
- return Rows.ToArray(r => r.Get<TValue>(columnIdx));
- }
- }
- #endregion
- /// <summary>
- /// Create a new row with data from <paramref name="obj"/>, and adds it to the table.
- /// </summary>
- /// <param name="obj"></param>
- /// <returns></returns>
- public CoreTable LoadRow(object obj)
- {
- var row = NewRow();
- FillRow(row, obj);
- Rows.Add(row);
- return this;
- }
- public int GetColumnIndex<T>(Expression<Func<T, object>> column)
- => GetColumnIndex(CoreUtils.GetFullPropertyName(column, "."));
- public int GetColumnIndex<T, TValue>(Expression<Func<T, TValue>> column)
- => GetColumnIndex(CoreUtils.GetFullPropertyName(column, "."));
- public int GetColumnIndex(string columnname)
- {
- for (var i = 0; i < Columns.Count; i++)
- if (Columns[i].ColumnName.Equals(columnname))
- {
- return i;
- }
- return -1;
- }
- #region ToLookup
- public IEnumerable<IGrouping<TKey, TValue>> ToGroups<T, TKey, TValue>(Expression<Func<T, TKey>> key,
- Expression<Func<T, TValue>> value)
- {
- return Rows.GroupBy(r => r.Get(key), r => r.Get(value));
- }
-
- public IMutableLookup<TKey, TValue> ToLookup<T, TKey, TValue>(Expression<Func<T, TKey>> key, Expression<Func<T, TValue>> value,
- Expression<Func<T, object>>? sort = null)
- {
- IMutableLookup<TKey, TValue> result = new MutableLookup<TKey, TValue>(
- Rows.ToLookup(
- r => r.Get(key),
- r => r.Get(value)
- )
- );
- return result;
- }
- public IMutableLookup<TKey, TValue> ToLookup<T, TKey, TValue>(Expression<Func<T, TKey>> key, Func<CoreRow, TValue> value,
- Expression<Func<T, object>>? sort = null)
- {
- IMutableLookup<TKey, TValue> result = new MutableLookup<TKey, TValue>(
- Rows.ToLookup(
- r => r.Get(key),
- r => value(r)
- )
- );
- return result;
- }
-
- #endregion
- #region Serialize Binary
- public void WriteBinary(CoreBinaryWriter writer, bool includeColumns)
- {
- writer.Write(TableName);
- if (includeColumns)
- {
- foreach (var column in Columns)
- {
- writer.Write(true);
- writer.Write(column.ColumnName);
- writer.Write(column.DataType.EntityName());
- }
- writer.Write(false);
- }
- writer.Write(Rows.Count);
- foreach (var row in Rows)
- {
- foreach (var col in Columns)
- {
- var val = row[col.ColumnName];
- writer.WriteBinaryValue(col.DataType, val);
- }
- }
- }
- public void SerializeBinary(CoreBinaryWriter writer) => WriteBinary(writer, true);
- public void ReadBinary(CoreBinaryReader reader, IList<CoreColumn>? columns)
- {
- tableName = reader.ReadString();
- Columns.Clear();
- if (columns is null)
- {
- while (reader.ReadBoolean())
- {
- var columnName = reader.ReadString();
- var dataType = CoreUtils.GetEntity(reader.ReadString());
- Columns.Add(new CoreColumn(dataType, columnName));
- }
- }
- else
- {
- foreach (var column in columns)
- {
- Columns.Add(column);
- }
- }
- Rows.Clear();
- var nRows = reader.ReadInt32();
- for (int i = 0; i < nRows; ++i)
- {
- var row = NewRow();
- foreach (var column in Columns)
- {
- var value = reader.ReadBinaryValue(column.DataType);
- row.Values.Add(value);
- }
- Rows.Add(row);
- }
- }
- public void DeserializeBinary(CoreBinaryReader reader) => ReadBinary(reader, null);
- #endregion
- }
- }
|