| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687 | using InABox.Clients;using InABox.Core;using System;using System.Collections.Generic;using System.Linq;using System.Reflection;using System.Text;using System.Threading.Tasks;namespace InABox.DynamicGrid;/// <summary>/// Defines a common interface for dealing with grids like <see cref="DynamicOneToManyGrid{TOne, TMany}"/>/// or <see cref="DynamicManyToManyGrid{TManyToMany, TThis}"/>, which display <see cref="Entity"/>s, but do not load them necessarily from the database,/// instead keeping them in memory./// <br/>/// This interface then allows other functions, like/// <see cref="DynamicMemoryEntityGridExtensions.EnsureColumns{T}(InABox.DynamicGrid.IDynamicMemoryEntityGrid{T}, Columns{T})"/>, to work based on <see cref="IDynamicMemoryEntityGrid{T}.LoadedColumns"/> and manage our column handling better./// </summary>/// <typeparam name="T"></typeparam>public interface IDynamicMemoryEntityGrid<T>    where T : Entity, IRemotable, IPersistent, new(){    /// <summary>    /// A set of columns representing which columns have been loaded from the database.    /// </summary>    /// <remarks>    /// This is used to refresh the data when the columns change.<br/>    ///     /// It is <see langword="null"/> if no data has been loaded from the database (that is, the data was gotten from    /// a page data handler instead.)    /// </remarks>    public HashSet<string>? LoadedColumns { get; }    public IEnumerable<T> Items { get; }}public static class DynamicMemoryEntityGridExtensions{    public static void EnsureColumns<T>(this IDynamicMemoryEntityGrid<T> grid, Columns<T> columns)        where T : Entity, IRemotable, IPersistent, new()    {        RequireColumns(grid, columns);        LoadForeignProperties(grid, columns);    }    /// <summary>    /// Load the properties of any <see cref="EntityLink{T}"/>s on this <see cref="TMany"/> where the <see cref="IEntityLink.ID"/> is not <see cref="Guid.Empty"/>.    /// This allows us to populate columns of transient objects, as long as they are linked by the ID. What this actually then does is query each    /// linked table with the required columns.    /// </summary>    /// <param name="columns"></param>    public static void LoadForeignProperties<T>(this IDynamicMemoryEntityGrid<T> grid, Columns<T> columns)        where T : Entity, IRemotable, IPersistent, new()    {        grid.Items.LoadForeignProperties(columns);    }    public static void RequireColumns<T>(this IDynamicMemoryEntityGrid<T> grid, Columns<T> columns)        where T : Entity, IRemotable, IPersistent, new()    {        if (grid.LoadedColumns is null) return;        // Figure out which columns we still need.        var newColumns = columns.Where(x => !grid.LoadedColumns.Contains(x.Property)).ToColumns(ColumnTypeFlags.None);        if (newColumns.Count > 0 && typeof(T).GetCustomAttribute<AutoEntity>() is null)        {            var data = Client.Query(                new Filter<T>(x => x.ID).InList(grid.Items.Select(x => x.ID).Where(x => x != Guid.Empty).ToArray()),                // We also need to add ID, so we know which item to fill.                newColumns.Add(x => x.ID));            foreach (var row in data.Rows)            {                var item = grid.Items.FirstOrDefault(x => x.ID == row.Get<T, Guid>(y => y.ID));                if (item is not null)                {                    row.FillObject(item, overrideExisting: false);                }            }            // Remember that we have now loaded this data.            foreach (var column in newColumns)            {                grid.LoadedColumns.Add(column.Property);            }        }    }}
 |