using EasyDevCore.Common; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Data.Common; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace EasyDevCore.Database.EntityTable { /// /// /// public static class DataTableExtensions { /// /// Loads the asynchronous. /// /// The source. /// The reader. /// The cancellation token. public static async Task LoadAsync(this DataTable source, DbDataReader reader, CancellationToken cancellationToken = default) { if (reader.IsClosed) return; int totalRows = source.Rows.Count; List> mapRcTcColumns = null; bool isFirst = true; while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) { if (isFirst) { Dictionary readerColumns = new Dictionary(StringComparer.InvariantCultureIgnoreCase); for (int i = 0; i < reader.FieldCount; i++) { readerColumns[reader.GetName(i)] = i; } if (source.Columns.Count == 0) { for (int col = 0; col < reader.FieldCount; col++) { source.Columns.Add(reader.GetName(col), reader.GetFieldType(col)); } } mapRcTcColumns = (from rc in readerColumns join tc in source.Columns.Cast() on rc.Key equals tc.ColumnName select new Tuple(rc.Value, tc.Ordinal)).ToList(); isFirst = false; } var newRow = source.NewRow(); for (int i = 0; i < mapRcTcColumns.Count; i++) { newRow[mapRcTcColumns[i].Item2] = reader.GetValue(mapRcTcColumns[i].Item1) ?? DBNull.Value; } source.Rows.Add(newRow); } bool hasNextResult = await reader.NextResultAsync(cancellationToken).ConfigureAwait(false); if (!hasNextResult) await reader.CloseAsync().ConfigureAwait(false); } /// /// Firsts the or add. /// /// The type of the source. /// The source. /// public static TSource FirstOrAdd(this DataTable source) where TSource : DataRow { if (source.Rows.Count == 0) { return (TSource)(source as DataTable).DefaultView.AddNew().Row; } else { return (TSource)(source as DataTable).DefaultView[0].Row; } } /// /// Ases the enumberable. /// /// The source. /// public static IEnumerable AsEnumberable(this DataTable source) { System.Collections.IEnumerator iterator = source.AsEnumberable().GetEnumerator(); while (iterator.MoveNext()) { yield return (DataRow)iterator.Current; } } /// /// Adds the new row. /// /// The source. /// public static DataRow AddNewRow(this DataTable source) { var newRow = source.NewRow(); source.Rows.Add(newRow); return newRow; } /// /// Finds the DataRow. /// /// The source. /// The values. /// public static DataRow FindDataRow(this DataTable source, params object[] values) { var argValues = ArgumentHelper.GetArgValues(values); DataView view = new DataView(source as DataTable, string.Empty, string.Join(",", argValues.Keys), DataViewRowState.CurrentRows); int rowIndex = view.Find(argValues.Values.ToArray()); return rowIndex == -1 ? null : view[rowIndex].Row; } /// /// Gets the index of the specified DataRow object. /// /// The source. /// The DataRow. /// public static int IndexOf(this DataTable source, DataRow DataRow) { return source.IndexOf(DataRow); } /// /// Gets the matched columns list. /// /// The source. /// The dest. /// The colum names filter (can be use = for mapping; ex: ColumnName = PropertyName). /// The except column names. /// internal static IDictionary GetMatchedColumnsList(this DataTable source, DataTable dest, string columnNames = "", string exceptColumnNames = "") { bool hasMapping = columnNames.Contains("="); IEnumerable colNames = columnNames.SplitCommaString(trimSpace: true); if (columnNames.Contains("*") || string.IsNullOrWhiteSpace(columnNames)) { colNames = from s in source.Columns.OfType() join d in dest.Columns.OfType() on s.ColumnName equals d.ColumnName select s.ColumnName; } HashSet exceptColNames = new HashSet(exceptColumnNames.SplitCommaString(trimSpace: true)); Dictionary mapColumns = colNames.Where(c => !exceptColNames.Contains(c)) .Select((s) => { if (hasMapping && s.Contains("=")) { var names = s.SplitString("=", trimSpace: true); return new { Key = names[0], Value = names[1] }; } return new { Key = s, Value = s }; }).ToDictionary(k => k.Key, v => v.Value); return mapColumns; } /// /// Gets the matched properties list. /// /// /// The source. /// The colum names filter (can be use = for mapping; ex: ColumnName = PropertyName). /// The except column names. /// The ignore prefix. /// IDictionary<System.String, PropertyDescriptor>. internal static IDictionary GetMatchedPropertiesList(this DataTable source, string columnNames = "", string exceptColumnNames = "", string ignorePrefix = "") { if (source == null) { return new Dictionary(); } Type DataRowType = typeof(T); bool mapAllColumns = columnNames.Contains("*") || string.IsNullOrWhiteSpace(columnNames); bool hasIgnorePrefix = !string.IsNullOrWhiteSpace(ignorePrefix); string[] ignorePrefixes = ignorePrefix.SplitByCommaOrSemiColon(); bool hasMapping = columnNames.Contains("="); bool hasExceptColumn = !string.IsNullOrWhiteSpace(exceptColumnNames); string[] exceptColNames = exceptColumnNames.SplitCommaString(trimSpace: true); Dictionary allProperties = TypeDescriptor.GetProperties(DataRowType).Cast().ToDictionary(p => { if (hasIgnorePrefix) { if (ignorePrefixes.Length > 1) { foreach (var prefix in ignorePrefixes) { if (prefix.StartsWith(prefix)) { return p.Name.Substring(prefix.Length); } } } else if (p.Name.StartsWith(ignorePrefix)) { return p.Name.Substring(ignorePrefix.Length); } } return p.Name; }); Dictionary colNames = columnNames.SplitCommaString(trimSpace: true) .Where(c => !mapAllColumns || !c.Contains("*")) .Select((s) => { if (hasMapping && s.Contains("=")) { var names = s.SplitString("=", trimSpace: true); return new { Key = names[0], Value = names[1] }; } return new { Key = s, Value = s }; }).ToDictionary(k => k.Key, v => v.Value); IDictionary mapProperties = source.Columns.Cast() .Select(c => new { c.ColumnName, PropertyName = colNames.ContainsKey(c.ColumnName) ? colNames[c.ColumnName] : c.ColumnName }) .Where(c => (mapAllColumns || colNames.ContainsKey(c.ColumnName)) && allProperties.ContainsKey(c.PropertyName) && (!hasExceptColumn || Array.IndexOf(exceptColNames, c.ColumnName) == -1) ) .ToDictionary(c => c.ColumnName, c => allProperties[c.PropertyName]); return mapProperties; } /// /// Imports from model list. /// /// /// The source. /// The list. /// The colum names filter (can be use = for mapping; ex: ColumnName = PropertyName). /// The except column names. /// The ignore prefix. public static void ImportFromModel(this DataTable source, IList list, string columnNames = "", string exceptColumnNames = "", string ignorePrefix = "") { Type DataRowType = typeof(T); IDictionary updateProperties = source.GetMatchedPropertiesList(columnNames, exceptColumnNames, ignorePrefix); foreach (T item in list) { var newRow = source.NewRow(); newRow.UpdateFromModel(item, updateProperties); source.Rows.Add(newRow); } } /// /// Convert to Model List. /// /// /// The source. /// The colum names filter (can be use = for mapping; ex: ColumnName = PropertyName). /// The except column names. /// The model status field has receive status of source (field type must be DataRowState). /// The ignore prefix. /// The method. /// IList<T>. public static IList ConvertToModel(this DataTable source, string columnNames = "", string exceptColumnNames = "", string modelStatusField = "", string ignorePrefix = "", Action method = null) { Type DataRowType = typeof(T); IDictionary updateProperties = source.GetMatchedPropertiesList(columnNames, exceptColumnNames, ignorePrefix); IList list = new List(); if (source != null) { foreach (DataRow DataRow in source.Rows) { list.Add(DataRow.ConvertToModel(updateProperties, modelStatusField, method: method)); } } return list; } /// /// Updates from DataRow. /// /// The source. /// The value. /// The colum names filter (can be use = for mapping; ex: ColumnName = PropertyName). /// The except column names. /// The key fields. (if not include key fields, auto use keys value to fields) /// public static bool UpdateFrom(this DataTable source, DataRow value, string columnNames = "", string exceptColumnNames = "", string keyFields = "") { string keysColumns = string.Empty; List columnList = new List(); List keysValues = new List(); if (string.IsNullOrWhiteSpace(keyFields)) { if (source.PrimaryKey?.Length > 0) { foreach (var key in source.PrimaryKey) { columnList.Add(key.ColumnName); keysColumns += (string.IsNullOrWhiteSpace(keysColumns) ? string.Empty : ",") + key.ColumnName; } } else { foreach (DataColumn col in source.Columns) { if (col.AutoIncrement) { columnList.Add(col.ColumnName); keysColumns += (string.IsNullOrWhiteSpace(keysColumns) ? string.Empty : ",") + col.ColumnName; } } } } else { keysColumns = keyFields; columnList.AddRange(keysColumns.SplitCommaString(trimSpace: true)); } foreach (string columnName in columnList) { keysValues.Add(value.GetValue(columnName)); } DataRow row = source.FindDataRow(keysColumns, keysValues.ToArray()); if (row != null) { row.UpdateFrom(value, columnNames, exceptColumnNames); return true; } return false; } /// /// Synches from table. /// /// The source. /// The values. /// The column update names. /// The column add new names. /// The except update column names. /// The except add new column names. /// The key fields. /// true if XXXX, false otherwise. public static void SynchFromTable(this DataTable source, DataTable values, string columnUpdateNames = "", string columnAddNewNames = "", string exceptUpdateColumnNames = "", string exceptAddNewColumnNames = "", string keyFields = "") { string keysColumns = string.Empty; List columnList = new List(); List keysValues = new List(); if (string.IsNullOrWhiteSpace(keyFields)) { if (source.PrimaryKey?.Length > 0) { foreach (var key in source.PrimaryKey) { columnList.Add(key.ColumnName); keysColumns += (string.IsNullOrWhiteSpace(keysColumns) ? string.Empty : ",") + key.ColumnName; } } else { foreach (DataColumn col in source.Columns) { if (col.AutoIncrement) { columnList.Add(col.ColumnName); keysColumns += (string.IsNullOrWhiteSpace(keysColumns) ? string.Empty : ",") + col.ColumnName; } } } } else { keysColumns = keyFields; columnList.AddRange(keysColumns.SplitCommaString(trimSpace: true)); } IDictionary updateColumns = values.GetMatchedColumnsList(source, columnUpdateNames, exceptUpdateColumnNames); IDictionary addNewColumns = values.GetMatchedColumnsList(source, columnAddNewNames, exceptAddNewColumnNames); DataView view = new DataView(source, string.Empty, keysColumns, DataViewRowState.CurrentRows); List listSynchItems = new List(); foreach (DataRow item in values.Rows) { keysValues.Clear(); foreach (string columnName in columnList) { keysValues.Add(item.GetValue(columnName)); } var rows = view.FindRows(keysValues.ToArray()); DataRow DataRow = rows != null && rows.Length > 0 ? rows[0].Row : null; if (DataRow != null) { foreach (var map in updateColumns) { DataRow.SetValue(map.Key, item[map.Value]); } listSynchItems.Add(DataRow); } else { DataRow = source.AddNewRow(); foreach (var map in addNewColumns) { DataRow.SetValue(map.Key, item[map.Value]); } listSynchItems.Add(DataRow); } } if (listSynchItems.Count != view.Count) { foreach (var deletedItem in source.AsEnumerable().Where(r => listSynchItems.IndexOf(r) < 0).ToArray()) { deletedItem.Delete(); } } } /// /// Synches from model. /// /// /// The source. /// The values. /// The column update names. /// The column add new names. /// The except update column names. /// The except add new column names. /// The key fields. /// The ignore prefix. public static void SynchFromModel(this DataTable source, IEnumerable values, string columnUpdateNames = "", string columnAddNewNames = "", string exceptUpdateColumnNames = "", string exceptAddNewColumnNames = "", string keyFields = "", string ignorePrefix = "") { string keysColumns = string.Empty; List columnList = new List(); List keysValues = new List(); if (string.IsNullOrWhiteSpace(keyFields)) { if (source.PrimaryKey?.Length > 0) { foreach (var key in source.PrimaryKey) { columnList.Add(key.ColumnName); keysColumns += (string.IsNullOrWhiteSpace(keysColumns) ? string.Empty : ",") + key.ColumnName; } } else { foreach (DataColumn col in source.Columns) { if (col.AutoIncrement) { columnList.Add(col.ColumnName); keysColumns += (string.IsNullOrWhiteSpace(keysColumns) ? string.Empty : ",") + col.ColumnName; } } } } else { keysColumns = keyFields; columnList.AddRange(keysColumns.SplitCommaString(trimSpace: true)); } IDictionary updateProperties = source.GetMatchedPropertiesList(columnUpdateNames, exceptUpdateColumnNames, ignorePrefix); IDictionary addNewProperties = source.GetMatchedPropertiesList(columnAddNewNames, exceptAddNewColumnNames, ignorePrefix); DataView view = new DataView(source, string.Empty, keysColumns, DataViewRowState.CurrentRows); List listSynchItems = new List(); foreach (T item in values) { keysValues.Clear(); foreach (string columnName in columnList) { keysValues.Add(item.GetPropertyValue(columnName)); } var rows = view.FindRows(keysValues.ToArray()); DataRow DataRow = rows != null && rows.Length > 0 ? rows[0].Row : null; if (DataRow != null) { listSynchItems.Add(DataRow); DataRow.UpdateFromModel(item, updateProperties); } else { DataRow = source.AddNewRow(); DataRow.UpdateFromModel(item, addNewProperties); listSynchItems.Add(DataRow); } } if (listSynchItems.Count != view.Count) { foreach (var deletedItem in source.AsEnumerable().Where(r => listSynchItems.IndexOf(r) < 0).ToArray()) { deletedItem.Delete(); } } } /// /// Updates from model. /// /// /// The source. /// The value. /// The colum names filter (can be use = for mapping; ex: ColumnName = PropertyName). /// The except column names. /// The key fields. /// The ignore prefix. /// true if XXXX, false otherwise. public static bool UpdateFromModel(this DataTable source, T value, string columnNames = "", string exceptColumnNames = "", string keyFields = "", string ignorePrefix = "") { string keysColumns = string.Empty; List columnList = new List(); List keysValues = new List(); if (string.IsNullOrWhiteSpace(keyFields)) { if (source.PrimaryKey?.Length > 0) { foreach (var key in source.PrimaryKey) { columnList.Add(key.ColumnName); keysColumns += (string.IsNullOrWhiteSpace(keysColumns) ? string.Empty : ",") + key.ColumnName; } } else { foreach (DataColumn col in source.Columns) { if (col.AutoIncrement) { columnList.Add(col.ColumnName); keysColumns += (string.IsNullOrWhiteSpace(keysColumns) ? string.Empty : ",") + col.ColumnName; } } } } else { keysColumns = keyFields; columnList.AddRange(keysColumns.SplitCommaString(trimSpace: true)); } foreach (string columnName in columnList) { keysValues.Add(value.GetPropertyValue(columnName)); } IDictionary updateProperties = source.GetMatchedPropertiesList(columnNames, exceptColumnNames, ignorePrefix); DataRow row = source.FindDataRow(keysColumns, keysValues.ToArray()); if (row != null) { row.UpdateFromModel(value, updateProperties); return true; } return false; } /// /// Updates from model. /// /// /// The source. /// The values. /// The colum names filter (can be use = for mapping; ex: ColumnName = PropertyName). /// The except column names. /// The key fields. /// The ignore prefix. public static void UpdateFromModel(this DataTable source, IEnumerable values, string columnNames = "", string exceptColumnNames = "", string keyFields = "", string ignorePrefix = "") { string keysColumns = string.Empty; List columnList = new List(); List keysValues = new List(); if (string.IsNullOrWhiteSpace(keyFields)) { if (source.PrimaryKey?.Length > 0) { foreach (var key in source.PrimaryKey) { columnList.Add(key.ColumnName); keysColumns += (string.IsNullOrWhiteSpace(keysColumns) ? string.Empty : ",") + key.ColumnName; } } else { foreach (DataColumn col in source.Columns) { if (col.AutoIncrement) { columnList.Add(col.ColumnName); keysColumns += (string.IsNullOrWhiteSpace(keysColumns) ? string.Empty : ",") + col.ColumnName; } } } } else { keysColumns = keyFields; columnList.AddRange(keysColumns.SplitCommaString(trimSpace: true)); } IDictionary updateProperties = source.GetMatchedPropertiesList(columnNames, exceptColumnNames, ignorePrefix); DataView view = new DataView(source, string.Empty, keysColumns, DataViewRowState.CurrentRows); foreach (T item in values) { keysValues.Clear(); foreach (string columnName in columnList) { keysValues.Add(item.GetPropertyValue(columnName)); } DataRow DataRow = view.FindRows(keysValues.ToArray())[0].Row; DataRow.UpdateFromModel(item, updateProperties); } } ///// ///// Import Raw XML to DataTable ///// ///// The source. ///// The XML. //public static void ImportFromRawXML(this DataTable source, string xml) //{ // ImportFromXML(source, null, xml); //} ///// ///// Import XML to DataTable ///// ///// The source. ///// Name of the root. ///// The XML. //public static void ImportFromRawXML(this DataTable source, string rootName, string xml) //{ // DataTable entities = DataTable.CreateFromRawXML(xml, rootName); // foreach (DataRow DataRow in entities) source.ImportFrom(entities); //} ///// ///// Import XML to DataTable ///// ///// The source. ///// The XML. //public static void ImportFromXML(this DataTable source, string xml) //{ // ImportFromXML(source, null, xml); //} ///// ///// Import XML to DataTable ///// ///// The source. ///// Name of the root. ///// The XML. //public static void ImportFromXML(this DataTable source, string rootName, string xml) //{ // DataTable entities = DataTable.CreateFromXML(xml, rootName); // foreach (DataRow DataRow in entities) source.ImportFrom(entities); //} } }