using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Drawing; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using InABox.Clients; using InABox.Configuration; using InABox.Core; using InABox.WPF; using Expression = System.Linq.Expressions.Expression; namespace InABox.DynamicGrid { public interface IDynamicDataGrid : IDynamicGrid { /// /// The tag the the DynamicGridColumns are stored against. If set to , /// the name of is used as a default. /// string? ColumnsTag { get; set; } IColumns LoadEditorColumns(); } public class DynamicDataGrid : DynamicGrid, IDynamicDataGrid where TEntity : Entity, IRemotable, IPersistent, new() { public delegate bool FilterSelected(DynamicGridFilter filter); public event FilterSelected OnFilterSelected; public delegate void OnReloadEventHandler(object sender, Filters criteria, Columns columns, ref SortOrder? sortby); private readonly int ChunkSize = 500; private readonly Button MergeBtn; private readonly Button FilterBtn; private bool _showFilterList; public bool ShowFilterList { get => _showFilterList; set { _showFilterList = value; if (FilterBtn != null) // FilterBtn can be null when ShowFilterList is set in DynamicGrid constructor FilterBtn.Visibility = value ? Visibility.Visible : Visibility.Collapsed; } } protected Tuple>? SelectedFilter; private Column[] FilterColumns; public DynamicDataGrid() : base() { FilterBtn = AddButton("", Wpf.Resources.filter.AsBitmapImage(), DoFilter); FilterBtn.Margin = new Thickness(0, 2, 7, 0); FilterBtn.Padding = new Thickness(0); FilterBtn.Visibility = ShowFilterList ? Visibility.Visible : Visibility.Collapsed; Options.BeginUpdate(); if (Security.CanEdit()) Options.Add(DynamicGridOption.AddRows).Add(DynamicGridOption.EditRows); if (Security.CanDelete()) Options.Add(DynamicGridOption.DeleteRows); if (Security.CanImport()) Options.Add(DynamicGridOption.ImportData); if (Security.CanExport()) Options.Add(DynamicGridOption.ExportData); if (Security.CanMerge()) Options.Add(DynamicGridOption.MultiSelect); Options.EndUpdate(); MergeBtn = AddButton("Merge", Wpf.Resources.merge.AsBitmapImage(Color.White), DoMerge); var fields = DatabaseSchema.Properties(typeof(TEntity)); foreach (var field in fields) if (!MasterColumns.Any(x => x.ColumnName == field.Name)) MasterColumns.Add(new DynamicGridColumn { ColumnName = field.Name }); var cols = LookupFactory.DefineColumns(); // Minimum Columns for Lookup values foreach (var col in cols.Items) HiddenColumns.Add(CoreUtils.CreateLambdaExpression(col.Property)); // Minimum Columns for Successful Saving // This should be cross-checked with the relevant Store<> // so that clients will (usually) provide sufficient columns for saving var required = LookupFactory.RequiredColumns(); foreach (var col in required.Items) HiddenColumns.Add(CoreUtils.CreateLambdaExpression(col.Property)); //HiddenColumns.Add(x => x.ID); if (typeof(TEntity).GetInterfaces().Contains(typeof(IIssues))) { HiddenColumns.Add(x => (x as IIssues)!.Issues); var coltype = typeof(DynamicIssuesColumn<>).MakeGenericType(typeof(TEntity)); ActionColumns.Add((Activator.CreateInstance(coltype, this) as DynamicActionColumn)!); } SetupFilterColumns(); } [MemberNotNull(nameof(FilterColumns))] private void SetupFilterColumns() { if (typeof(TEntity).GetCustomAttribute() is AutoEntity auto) { if (auto.Generator is not null) { var columns = auto.Generator.IDColumns; FilterColumns = columns.Select(x => new Column(x.Property)).ToArray(); } else { FilterColumns = Array.Empty>(); } } else { FilterColumns = new[] { new Column(x => x.ID) }; } foreach (var column in FilterColumns) { AddHiddenColumn(column.Property); } } protected override void BeforeLoad(IDynamicEditorForm form) { form.ReadOnly = !Security.CanEdit(); base.BeforeLoad(form); } public string? ColumnsTag { get; set; } protected override void OptionsChanged(object sender, EventArgs args) { base.OptionsChanged(sender, args); if (MergeBtn != null) MergeBtn.Visibility = Visibility.Collapsed; ShowFilterList = Options.Contains(DynamicGridOption.FilterRows); } protected override void SelectItems(CoreRow[]? rows) { base.SelectItems(rows); MergeBtn.Visibility = Options.Contains(DynamicGridOption.MultiSelect) && typeof(TEntity).IsAssignableTo(typeof(IMergeable)) && Security.CanMerge() && rows != null && rows.Length > 1 ? Visibility.Visible : Visibility.Collapsed; } /*private Filter? CreateFilter(Dictionary criteria) where T : Entity { Filter? filter = null; foreach (var key in criteria.Keys) { var exp = CoreUtils.GetPropertyExpression(key); var flt = new Filter(exp).IsEqualTo(criteria[key]); if (filter != null) filter.Ands.Add(flt); else filter = flt; } return filter; } private Columns CreateColumns(List columnnames) where T : Entity { var expressions = new List>>(); foreach (var columnname in columnnames) try { var expression = CoreUtils.GetPropertyExpression(columnname); expressions.Add(expression); } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } if (!columnnames.Contains("ID")) expressions.Add(CoreUtils.GetPropertyExpression("ID")); //if (!columnnames.Contains("Attributes")) // expressions.Add(CoreUtils.GetPropertyExpression("Attributes")); return new Columns(expressions.ToArray()); } private SortOrder? CreateSortOrder(string columnname) where T : Entity { if (string.IsNullOrWhiteSpace(columnname)) return null; var expression = CoreUtils.GetPropertyExpression(columnname); return new SortOrder(expression); }*/ public event OnReloadEventHandler? OnReload; protected override void Reload(Filters criteria, Columns columns, ref SortOrder? sort, Action action) { //if (sort == null) // sort = new SortOrder(x => x.Sort); if (SelectedFilter != null) criteria.Add(SelectedFilter.Item2); OnReload?.Invoke(this, criteria, columns, ref sort); new Client().Query(criteria.Combine(), columns, sort, action); } private CoreRow[]? SelectedBeforeRefresh; protected override void OnAfterRefresh() { base.OnAfterRefresh(); if (SelectedBeforeRefresh is not null) { var selectedValues = SelectedBeforeRefresh .Select(x => FilterColumns.Select(c => x[c.Property]).ToList()) .ToList(); SelectedBeforeRefresh = null; var selectedRows = Data.Rows.Where(r => { return selectedValues.Any(v => { for (int i = 0; i < v.Count; ++i) { if (v[i]?.Equals(r[FilterColumns[i].Property]) != true) return false; } return true; }); }).ToArray(); SelectedRows = selectedRows; SelectItems(selectedRows); } } public override void Refresh(bool reloadcolumns, bool reloaddata) { SelectedBeforeRefresh = SelectedRows; base.Refresh(reloadcolumns, reloaddata); } IColumns IDynamicDataGrid.LoadEditorColumns() => LoadEditorColumns(); public Columns LoadEditorColumns() { var result = new Columns().Default( ColumnType.IncludeOptional, ColumnType.IncludeForeignKeys, ColumnType.IncludeUserProperties, ColumnType.IncludeEditable); var cols = DataColumns(); foreach (var col in cols.Items) if (!result.Items.Any(x => string.Equals(x.Property, col.Property))) result.Add(col.Property); var props = DatabaseSchema.Properties(typeof(TEntity)) .Where(x => x.Setter() != null) .OrderBy(x => CoreUtils.GetPropertySequence(typeof(TEntity), x.Name)); foreach (var col in result.Items) { var prop = DatabaseSchema.Property(typeof(TEntity), col.Property); if (prop?.Editor is DataLookupEditor dataLookup) { foreach (var lookupColumn in LookupFactory.DefineFilterColumns(dataLookup.Type).ColumnNames()) { result.Add(lookupColumn); } } } return result; } private Filter? GetRowFilter(CoreRow row) { var newFilters = new Filters(); foreach (var column in FilterColumns) { newFilters.Add(new Filter(column.Property).IsEqualTo(row[column.Property])); } return newFilters.Combine(); } protected override TEntity[] LoadItems(CoreRow[] rows) { Filter? filter = null; var results = new List(); for (var i = 0; i < rows.Length; i = i + ChunkSize) { var chunk = rows.Skip(i).Take(ChunkSize); foreach (var row in chunk) { var newFilter = GetRowFilter(row); if (newFilter is not null) { //var id = row.Get(x => x.ID); if (filter is null) filter = newFilter;//new Filter(x => x.ID).IsEqualTo(id); else filter = filter.Or(newFilter);//.Or(x => x.ID).IsEqualTo(id); } } var columns = LoadEditorColumns(); // new Columns().Default(ColumnType.IncludeOptional, ColumnType.IncludeForeignKeys, ColumnType.IncludeUserProperties); var data = new Client().Query(filter, columns); results.AddRange(data.Rows.Select(x => x.ToObject())); } return results.ToArray(); } protected override TEntity LoadItem(CoreRow row) { var id = row.Get(x => x.ID); var filter = GetRowFilter(row);//new Filter(x => x.ID).IsEqualTo(id); return new Client().Load(filter).FirstOrDefault() ?? throw new Exception($"No {typeof(TEntity).Name} with ID {id}"); } public override void SaveItem(TEntity item) { new Client().Save(item, "Edited by User"); } public override void SaveItems(TEntity[] items) { new Client().Save(items, "Edited by User"); } protected override void DeleteItems(params CoreRow[] rows) { var deletes = new List(); foreach (var row in rows) { var delete = new TEntity(); foreach (var column in FilterColumns) { CoreUtils.SetPropertyValue(delete, column.Property, row[column.Property]); } //var delete = /* row.ToObject(); */ new TEntity { ID = row.Get(x => x.ID) }; deletes.Add(delete); } new Client().Delete(deletes, "Deleted on User Request"); } private object GetPropertyValue(Expression member, object obj) { var objectMember = Expression.Convert(member, typeof(object)); var getterLambda = Expression.Lambda>(objectMember); var getter = getterLambda.Compile(); return getter(); } protected override void ObjectToRow(TEntity obj, CoreRow row) { foreach (var column in Data.Columns) { var prop = DatabaseSchema.Property(obj.GetType(), column.ColumnName); if (prop is StandardProperty) row.Set(column.ColumnName, CoreUtils.GetPropertyValue(obj, prop.Name)); else if (prop is CustomProperty) row.Set(column.ColumnName, obj.UserProperties[column.ColumnName]); } //base.ObjectToRow(obj, row); } //private void Auditgrid_OnReload(object sender, Dictionary criteria, ref String sort) //{ // criteria["DocumentType"] = typeof(TEntity).EntityName(); // criteria["DocumentID"] = (sender as FrameworkElement).Tag; // sort = "TimeStamp"; //} public override void LoadEditorButtons(TEntity item, DynamicEditorButtons buttons) { base.LoadEditorButtons(item, buttons); if (ClientFactory.IsSupported()) buttons.Add("Audit Trail", Wpf.Resources.view.AsBitmapImage(), item, AuditTrailClick); } private void AuditTrailClick(object sender, object item) { var entity = (TEntity)item; var window = new AuditWindow(entity.ID); window.ShowDialog(); } protected override void LoadColumnsMenu(ContextMenu menu) { base.LoadColumnsMenu(menu); //menu.Items.Add(new Separator()); var ResetColumns = new MenuItem { Header = "Reset Columns to Default" }; ResetColumns.Click += ResetColumnsClick; menu.Items.Add(ResetColumns); if (Security.IsAllowed()) { menu.Items.Add(new Separator()); var UpdateDefaultColumns = new MenuItem { Header = "Mark Columns as Default" }; UpdateDefaultColumns.Click += UpdateDefaultColumnsClick; menu.Items.Add(UpdateDefaultColumns); } } private void ResetColumnsClick(object sender, RoutedEventArgs e) { VisibleColumns.Clear(); SaveColumns(VisibleColumns); Refresh(true, true); } private void UpdateDefaultColumnsClick(object sender, RoutedEventArgs e) { var tag = GetTag(); new GlobalConfiguration(tag).Save(VisibleColumns); new UserConfiguration(tag).Delete(); Refresh(true, true); } private string GetTag() { var tag = typeof(TEntity).Name; if (!string.IsNullOrWhiteSpace(ColumnsTag)) tag = string.Format("{0}.{1}", tag, ColumnsTag); return tag; } private BaseEditor GetColumnEditor(DynamicGridColumn column) { try { var prop = DatabaseSchema.Property(typeof(TEntity), column.ColumnName); if (prop != null) return prop.Editor; var type = CoreUtils.GetProperty(typeof(TEntity), column.ColumnName); return type.GetEditor() ?? new NullEditor(); } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } return new NullEditor(); } protected override DynamicGridColumns LoadColumns() { var tag = GetTag(); var user = Task.Run(() => new UserConfiguration(tag).Load()); user.Wait(); var global = Task.Run(() => new GlobalConfiguration(tag).Load()); global.Wait(); //Task.WaitAll(user, global); var columns = user.Result.Any() ? user.Result : global.Result; if (!columns.Any()) GenerateColumns(columns); //override this to provide specific columns on startup var removes = columns.Where(x => x is null || string.IsNullOrWhiteSpace(x.ColumnName) || DatabaseSchema.Property(typeof(TEntity), x.ColumnName) == null || GetColumnEditor(x) is NullEditor) .ToArray(); foreach (var remove in removes) columns.Remove(remove); if (columns.Count == 0) columns.AddRange(base.LoadColumns()); foreach (var column in columns) try { var prop = DatabaseSchema.Property(typeof(TEntity), column.ColumnName); if (prop != null) { column.Editor = prop.Editor; } else { var type = CoreUtils.GetProperty(typeof(TEntity), column.ColumnName); column.Editor = type.GetEditor() ?? new NullEditor(); } } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } columns.RemoveAll(x => DatabaseSchema.Property(typeof(TEntity), x.ColumnName) == null || GetColumnEditor(x) is NullEditor); return columns; } protected virtual void GenerateColumns(DynamicGridColumns columns) { var cols = new Columns().Default(Options.Contains(DynamicGridOption.DirectEdit) ? new[] { ColumnType.IncludeForeignKeys, ColumnType.ExcludeID } : new ColumnType[] { ColumnType.IncludeLinked, ColumnType.IncludeNestedLinks, ColumnType.IncludeFormulae, ColumnType.IncludeAggregates, ColumnType.ExcludeID }); if (cols != null) { foreach (var col in cols.Items) { var mc = MasterColumns.FirstOrDefault(x => x.ColumnName.Equals(col.Property)); if (mc != null && !(mc.Editor is NullEditor) && mc.Editor.Visible != Visible.Hidden) columns.Add(mc); } } } protected override void SaveColumns(DynamicGridColumns columns) { var tag = GetTag(); new UserConfiguration(tag).Save(columns); } protected override BaseEditor? GetEditor(object item, DynamicGridColumn column) { var prop = DatabaseSchema.Properties(typeof(TEntity)).FirstOrDefault(x => x.Name == column.ColumnName); if (prop != null) return prop.Editor; return base.GetEditor(item, column); } protected override object? GetEditorValue(object item, string name) { if (item is TEntity entity) { var prop = DatabaseSchema.Properties(typeof(TEntity)).FirstOrDefault(x => x.Name == name); if (prop is CustomProperty) { if (entity.UserProperties.ContainsKey(name)) return entity.UserProperties[name]; return null; } } return base.GetEditorValue(item, name); } protected override void SetEditorValue(object item, string name, object value) { var prop = DatabaseSchema.Properties(typeof(TEntity)).FirstOrDefault(x => x.Name == name); if (prop is CustomProperty && item is TEntity entity) { entity.UserProperties[name] = value; } else { base.SetEditorValue(item, name, value); } } protected bool Duplicate( CoreRow row, Expression> codefield, Type[] childtypes) { var id = row.Get(x => x.ID); var code = row.Get(codefield) as string; var tasks = new List(); var itemtask = Task.Run(() => { var filter = new Filter(x => x.ID).IsEqualTo(id); var result = new Client().Load(filter).FirstOrDefault() ?? throw new Exception("Entity does not exist!"); return result; }); tasks.Add(itemtask); //itemtask.Wait(); Task>? codetask = null; if (!string.IsNullOrWhiteSpace(code)) { codetask = Task.Run(() => { var columns = new Columns(codefield); //columns.Add(codefield); var filter = new Filter(codefield).BeginsWith(code); var table = new Client().Query(filter, columns); var result = table.Rows.Select(x => x.Get(codefield) as string).ToList(); return result; }); tasks.Add(codetask); } //codetask.Wait(); var children = new Dictionary(); foreach (var childtype in childtypes) { var childtask = Task.Run(() => { var prop = childtype.GetProperties().FirstOrDefault(x => x.PropertyType.GetInterfaces().Contains(typeof(IEntityLink)) && x.PropertyType.GetInheritedGenericTypeArguments().FirstOrDefault() == typeof(TEntity)); if (prop is not null) { var filter = Core.Filter.Create(childtype); filter.Expression = CoreUtils.GetMemberExpression(childtype, prop.Name + ".ID"); filter.Operator = Operator.IsEqualTo; filter.Value = id; var sort = LookupFactory.DefineSort(childtype); var client = ClientFactory.CreateClient(childtype); var table = client.Query(filter, null, sort); foreach (var r in table.Rows) { r["ID"] = Guid.Empty; r[prop.Name + ".ID"] = Guid.Empty; } children[childtype] = table; } else { Logger.Send(LogType.Error, "", $"DynamicDataGrid<{typeof(TEntity)}>.Duplicate(): No parent property found for child type {childtype}"); } }); tasks.Add(childtask); //childtask.Wait(); } //var manytomanys = CoreUtils.TypeList( // AppDomain.CurrentDomain.GetAssemblies(), // x => x.GetInterfaces().Any(intf => intf.IsGenericType && intf.GetGenericTypeDefinition() == typeof(IManyToMany<,>) && intf.GenericTypeArguments.Contains(typeof(T))) //); //Task childtask = Task.Run(() => //{ // var result = new CoreTable(); // result.LoadColumns(typeof(TChild)); // var children = new Client().Load(new Filter(x => linkfield).IsEqualTo(id)); // foreach (var child in children) // { // child.ID = Guid.Empty; // String linkprop = CoreUtils.GetFullPropertyName(linkfield, "."); // CoreUtils.SetPropertyValue(child, linkprop, Guid.Empty); // var newrow = result.NewRow(); // result.LoadRow(newrow, child); // result.Rows.Add(newrow); // } // return result; //}); //tasks.Add(childtask); Task.WaitAll(tasks.ToArray()); var item = itemtask.Result; item.ID = Guid.Empty; if (codetask != null) { var codes = codetask.Result; var i = 1; while (codes.Contains(string.Format("{0} ({1})", code, i))) i++; var codeprop = CoreUtils.GetFullPropertyName(codefield, "."); CoreUtils.SetPropertyValue(item, codeprop, string.Format("{0} ({1})", code, i)); } var grid = new DynamicDataGrid(); return grid.EditItems(new[] { item }, t => children.ContainsKey(t) ? children[t] : null, true); } private bool DoFilter(Button button, CoreRow[] rows) { var menu = new ContextMenu(); var none = new MenuItem { Header = "Clear Filters", Tag = null }; none.Click += Filter_Click; menu.Items.Add(none); var tag = GetTag(); var filters = new GlobalConfiguration(tag).Load(); foreach (var filter in filters) { var item = new MenuItem { Header = filter.Name, Tag = filter }; if (SelectedFilter?.Item1 == filter.Name) { item.IsChecked = true; } item.Click += Filter_Click; menu.Items.Add(item); } if (Security.IsAllowed()) { menu.Items.Add(new Separator()); var manage = new MenuItem { Header = "Manage Filters" }; manage.Click += (s, e) => { var window = new DynamicGridFilterEditor(filters, typeof(TEntity)); if (window.ShowDialog() == true) { new GlobalConfiguration(tag).Save(filters); } }; menu.Items.Add(manage); } menu.IsOpen = true; return false; } private void Filter_Click(object sender, RoutedEventArgs e) { var tag = (sender as MenuItem)?.Tag; string text = ""; Bitmap image; if (tag is DynamicGridFilter filter) { SelectedFilter = new(filter.Name, Serialization.Deserialize>(filter.Filter)); image = Wpf.Resources.filter_set; if ((bool)(OnFilterSelected?.Invoke(filter))) text = filter.Name; } else { SelectedFilter = null; image = Wpf.Resources.filter; OnFilterSelected?.Invoke(new DynamicGridFilter()); } UpdateButton(FilterBtn, image.AsBitmapImage(), text); Refresh(false, true); } private bool DoMerge(Button arg1, CoreRow[] arg2) { if (arg2 == null || arg2.Length <= 1) return false; var targetid = arg2.Last().Get(x => x.ID); var target = arg2.Last().ToObject().ToString(); var otherids = arg2.Select(r => r.Get(x => x.ID)).Where(x => x != targetid).ToArray(); string[] others = arg2.Where(r => otherids.Contains(r.Get("ID"))).Select(x => x.ToObject().ToString()!).ToArray(); var rows = arg2.Length; if (MessageBox.Show( string.Format( "This will merge the following items:\n\n- {0}\n\n into:\n\n- {1}\n\nAfter this, the items will be permanently removed.\nAre you sure you wish to do this?", string.Join("\n- ", others), target ), "Merge Items Warning", MessageBoxButton.YesNo, MessageBoxImage.Stop) != MessageBoxResult.Yes ) return false; using (new WaitCursor()) { var types = CoreUtils.TypeList( AppDomain.CurrentDomain.GetAssemblies(), x => x.IsClass && !x.IsGenericType && x.IsSubclassOf(typeof(Entity)) && !x.Equals(typeof(AuditTrail)) && !x.Equals(typeof(TEntity)) && x.GetCustomAttribute() == null ).ToArray(); foreach (var type in types) { var props = CoreUtils.PropertyList( type, x => x.PropertyType.GetInterfaces().Contains(typeof(IEntityLink)) && x.PropertyType.GetInheritedGenericTypeArguments().Contains(typeof(TEntity)) ); foreach (var prop in props) { var propname = string.Format(prop.Name + ".ID"); var filter = Core.Filter.Create(type); filter.Expression = CoreUtils.CreateMemberExpression(type, propname); filter.Operator = Operator.InList; filter.Value = otherids; var columns = Columns.Create(type) .Add("ID") .Add(propname); var updates = ClientFactory.CreateClient(type).Query(filter, columns).Rows.Select(r => r.ToObject(type)).ToArray(); if (updates.Any()) { foreach (var update in updates) CoreUtils.SetPropertyValue(update, propname, targetid); ClientFactory.CreateClient(type).Save(updates, string.Format("Merged {0} Records", typeof(TEntity).EntityName().Split('.').Last())); } } } var histories = new Client() .Query(new Filter(x => x.EntityID).InList(otherids), new Columns(x => x.EntityID)).Rows .Select(x => x.ToObject()).ToArray(); foreach (var history in histories) history.EntityID = targetid; if (histories.Any()) new Client().Save(histories, ""); var deletes = new List(); foreach (var otherid in otherids) { var delete = new TEntity(); CoreUtils.SetPropertyValue(delete, "ID", otherid); deletes.Add(delete); } ClientFactory.CreateClient(typeof(TEntity)) .Delete(deletes, string.Format("Merged {0} Records", typeof(TEntity).EntityName().Split('.').Last())); return true; } } protected override IEnumerable LoadDuplicatorItems(CoreRow[] rows) { return rows.Select(x => x.ToObject()); } protected override bool BeforePaste(IEnumerable items, ClipAction action) { if (action == ClipAction.Copy) { foreach (var item in items) item.ID = Guid.Empty; return true; } return base.BeforePaste(items, action); } protected override void CustomiseExportFilters(Filters filters, CoreRow[] visiblerows) { base.CustomiseExportFilters(filters, visiblerows); /*if (IsEntity) { var ids = visiblerows.Select(r => r.Get(c => c.ID)).ToArray(); filters.Add(new Filter(x => x.ID).InList(ids)); } else { Filter? filter = null; foreach (var row in visiblerows) { var rowFilter = GetRowFilter(row); if(rowFilter is not null) { if (filter is null) { filter = rowFilter; } else { filter.Or(rowFilter); } } } filters.Add(filter); }*/ } protected override IEnumerable> LoadExportTables(Filters filter, IEnumerable> tableColumns) { var queries = new Dictionary(); var columns = tableColumns.ToList(); foreach (var table in columns) { var tableType = table.Item1; PropertyInfo? property = null; var m2m = CoreUtils.GetManyToMany(tableType, typeof(TEntity)); IFilter? queryFilter = null; if (m2m != null) { property = CoreUtils.GetManyToManyThisProperty(tableType, typeof(TEntity)); } else { var o2m = CoreUtils.GetOneToMany(tableType, typeof(TEntity)); if (o2m != null) { property = CoreUtils.GetOneToManyProperty(tableType, typeof(TEntity)); } } if (property != null) { var subQuery = new SubQuery(); subQuery.Filter = filter.Combine(); subQuery.Column = new Column(x => x.ID); queryFilter = (Activator.CreateInstance(typeof(Filter<>).MakeGenericType(tableType)) as IFilter)!; queryFilter.Expression = CoreUtils.GetMemberExpression(tableType, property.Name + ".ID"); queryFilter.InQuery(subQuery); queries[tableType.Name] = new QueryDef(tableType) { Filter = queryFilter, Columns = table.Item2 }; } } var results = Client.QueryMultiple(queries); return columns.Select(x => new Tuple(x.Item1, results[x.Item1.Name])); } protected override CoreTable LoadImportKeys(String[] fields) { return new Client().Query(null, new Columns(fields)); } public void UpdateFilterButton(Bitmap image, string text = "") { UpdateButton(FilterBtn, image.AsBitmapImage(), text); } } }