Quellcode durchsuchen

Merge remote-tracking branch 'origin/kenric' into frank

frogsoftware vor 10 Monaten
Ursprung
Commit
8b6f527412

+ 118 - 0
InABox.Core/Client/Client.cs

@@ -35,6 +35,24 @@ namespace InABox.Clients
             where T: BaseObject, new() 
             => Results[typeof(T).Name].ToObjects<T>();
 
+        /// <summary>
+        /// Like <see cref="Get{T}"/>, but calls <see cref="CoreTable.ToArray{T}"/> on the table.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <returns></returns>
+        public T[] GetArray<T>()
+            where T: BaseObject, new() 
+            => Results[typeof(T).Name].ToArray<T>();
+
+        /// <summary>
+        /// Like <see cref="Get{T}"/>, but calls <see cref="CoreTable.ToList{T}"/> on the table.
+        /// </summary>
+        /// <typeparam name="T"></typeparam>
+        /// <returns></returns>
+        public List<T> GetList<T>()
+            where T: BaseObject, new() 
+            => Results[typeof(T).Name].ToList<T>();
+
         public CoreTable Get(string name) => Results[name];
 
         public CoreTable GetOrDefault(string name) => Results.GetValueOrDefault(name);
@@ -659,4 +677,104 @@ namespace InABox.Clients
             }
         }
     }
+
+    public static class ClientExtensions
+    {
+        /// <summary>
+        /// Load the properties of any <see cref="EntityLink{T}"/>s on this <typeparamref name="T"/> 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 IEnumerable<T> items, Columns<T> columns)
+            where T : BaseObject, new()
+        {
+            // Lists of properties that we need, arranged by the entity link property which is their parent.
+            // LinkIDProperty : (Type, Properties: [(columnName, property)], Objects)
+            var newData = new Dictionary<IProperty, Tuple<Type, List<Tuple<string, IProperty>>, HashSet<T>>>();
+
+            foreach (var column in columns)
+            {
+                var property = DatabaseSchema.Property(typeof(T), column.Property);
+                if (property?.GetOuterParent(x => x.IsEntityLink) is IProperty linkProperty)
+                {
+                    var remaining = column.Property[(linkProperty.Name.Length + 1)..];
+                    if (remaining.Equals(nameof(IEntityLink.ID)))
+                    {
+                        // This guy isn't foreign, so we don't pull him.
+                        continue;
+                    }
+
+                    var idProperty = DatabaseSchema.Property(typeof(T), linkProperty.Name + "." + nameof(IEntityLink.ID))!;
+
+                    var linkType = linkProperty.PropertyType.GetInterfaceDefinition(typeof(IEntityLink<>))!.GenericTypeArguments[0];
+                    if (!newData.TryGetValue(idProperty, out var data))
+                    {
+                        data = new Tuple<Type, List<Tuple<string, IProperty>>, HashSet<T>>(
+                            linkType,
+                            new List<Tuple<string, IProperty>>(),
+                            new HashSet<T>());
+                        newData.Add(idProperty, data);
+                    }
+
+                    var any = false;
+                    foreach (var item in items)
+                    {
+                        if (!item.LoadedColumns.Contains(column.Property))
+                        {
+                            var linkID = (Guid)idProperty.Getter()(item);
+                            if (linkID != Guid.Empty)
+                            {
+                                any = true;
+                                data.Item3.Add(item);
+                            }
+                        }
+                    }
+                    if (any)
+                    {
+                        data.Item2.Add(new Tuple<string, IProperty>(remaining, property));
+                    }
+                }
+            }
+
+            var queryDefs = new List<IKeyedQueryDef>();
+            foreach (var (prop, data) in newData)
+            {
+                if (data.Item2.Count != 0)
+                {
+                    var ids = data.Item3.Select(prop.Getter()).Cast<Guid>().ToArray();
+                    queryDefs.Add(new KeyedQueryDef(prop.Name, data.Item1,
+                        Filter.Create<Entity>(data.Item1, x => x.ID).InList(ids),
+                        Columns.None(data.Item1)
+                            .Add(data.Item2.Select(x => x.Item1))
+                            .Add<Entity>(x => x.ID)));
+                }
+            }
+            var results = Client.QueryMultiple(queryDefs);
+            foreach(var (prop, data) in newData)
+            {
+                var table = results.GetOrDefault(prop.Name);
+                if(table is null)
+                {
+                    continue;
+                }
+                foreach (var entity in data.Item3)
+                {
+                    var linkID = (Guid)prop.Getter()(entity);
+                    var row = table.Rows.FirstOrDefault(x => x.Get<Entity, Guid>(x => x.ID) == linkID);
+                    if (row != null)
+                    {
+                        foreach (var (name, property) in data.Item2)
+                        {
+                            if (!entity.LoadedColumns.Contains(property.Name))
+                            {
+                                property.Setter()(entity, row[name]);
+                                entity.LoadedColumns.Add(property.Name);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
 }

+ 18 - 11
inabox.database.sqlite/SQLiteProvider.cs

@@ -1513,23 +1513,30 @@ public class SQLiteProvider : IProvider
 
             if (filter.Operator == Operator.InList || filter.Operator == Operator.NotInList)
             {
-                // if, and only if the list contains Guids, we can safely bypass the 
-                // 1000-parameter limit by using building the string ourselves
-                if (filter.Value is Guid[] list)
+                if(filter.Value is Array arr && arr.Length == 0)
                 {
-                    result = string.Format("(" + operators[filter.Operator] + ")", prop, string.Format("\"{0}\"", string.Join("\",\"", list)));
+                    result = filter.Operator == Operator.InList ? "1 = 0" : "1 = 1";
                 }
-                else if (filter.Value is IEnumerable enumerable)
+                else
                 {
-                    var paramlist = new List<object>();
-                    foreach (var item in enumerable)
+                    // if, and only if the list contains Guids, we can safely bypass the 
+                    // 1000-parameter limit by using building the string ourselves
+                    if (filter.Value is Guid[] list)
                     {
-                        var sParam = string.Format("@p{0}", command.Parameters.Count);
-                        command.Parameters.AddWithValue(sParam, Encode(item, mexp.Type));
-                        paramlist.Add(sParam);
+                        result = string.Format("(" + operators[filter.Operator] + ")", prop, string.Format("\"{0}\"", string.Join("\",\"", list)));
                     }
+                    else if (filter.Value is IEnumerable enumerable)
+                    {
+                        var paramlist = new List<object>();
+                        foreach (var item in enumerable)
+                        {
+                            var sParam = string.Format("@p{0}", command.Parameters.Count);
+                            command.Parameters.AddWithValue(sParam, Encode(item, mexp.Type));
+                            paramlist.Add(sParam);
+                        }
 
-                    result = string.Format("(" + operators[filter.Operator] + ")", prop, string.Join(",", paramlist));
+                        result = string.Format("(" + operators[filter.Operator] + ")", prop, string.Join(",", paramlist));
+                    }
                 }
             }
             else if (filter.Operator == Operator.InQuery || filter.Operator == Operator.NotInQuery)

+ 1 - 8
inabox.wpf/DynamicGrid/DynamicGrid.cs

@@ -1336,14 +1336,7 @@ public abstract class DynamicGrid<T> : DynamicGrid, IDynamicGridUIComponentParen
 
     public virtual T[] LoadItems(CoreRow[] rows)
     {
-        var result = new List<T>();
-        foreach (var row in rows)
-        {
-            var index = Data.Rows.IndexOf(row);
-            result.Add(LoadItem(row));
-        }
-
-        return result.ToArray();
+        return rows.ToArray(LoadItem);
     }
 
     public abstract T LoadItem(CoreRow row);

+ 1 - 86
inabox.wpf/DynamicGrid/IDynamicMemoryEntityGrid.cs

@@ -53,92 +53,7 @@ public static class DynamicMemoryEntityGridExtensions
     public static void LoadForeignProperties<T>(this IDynamicMemoryEntityGrid<T> grid, Columns<T> columns)
         where T : Entity, IRemotable, IPersistent, new()
     {
-        // Lists of properties that we need, arranged by the entity link property which is their parent.
-        // LinkIDProperty : (Type, Properties: [(columnName, property)], Objects)
-        var newData = new Dictionary<IProperty, Tuple<Type, List<Tuple<string, IProperty>>, HashSet<T>>>();
-
-        foreach (var column in columns)
-        {
-            var property = DatabaseSchema.Property(typeof(T), column.Property);
-            if (property?.GetOuterParent(x => x.IsEntityLink) is IProperty linkProperty)
-            {
-                var remaining = column.Property[(linkProperty.Name.Length + 1)..];
-                if (remaining.Equals(nameof(IEntityLink.ID)))
-                {
-                    // This guy isn't foreign, so we don't pull him.
-                    continue;
-                }
-
-                var idProperty = DatabaseSchema.Property(typeof(T), linkProperty.Name + "." + nameof(IEntityLink.ID))!;
-
-                var linkType = linkProperty.PropertyType.GetInterfaceDefinition(typeof(IEntityLink<>))!.GenericTypeArguments[0];
-                if (!newData.TryGetValue(idProperty, out var data))
-                {
-                    data = new Tuple<Type, List<Tuple<string, IProperty>>, HashSet<T>>(
-                        linkType,
-                        new List<Tuple<string, IProperty>>(),
-                        new HashSet<T>());
-                    newData.Add(idProperty, data);
-                }
-
-                var any = false;
-                foreach (var item in grid.Items)
-                {
-                    if (!item.LoadedColumns.Contains(column.Property))
-                    {
-                        var linkID = (Guid)idProperty.Getter()(item);
-                        if (linkID != Guid.Empty)
-                        {
-                            any = true;
-                            data.Item3.Add(item);
-                        }
-                    }
-                }
-                if (any)
-                {
-                    data.Item2.Add(new(remaining, property));
-                }
-            }
-        }
-
-        var queryDefs = new List<IKeyedQueryDef>();
-        foreach (var (prop, data) in newData)
-        {
-            if (data.Item2.Count != 0)
-            {
-                var ids = data.Item3.Select(prop.Getter()).Cast<Guid>().ToArray();
-                queryDefs.Add(new KeyedQueryDef(prop.Name, data.Item1,
-                    Filter.Create<Entity>(data.Item1, x => x.ID).InList(ids),
-                    Columns.None(data.Item1)
-                        .Add(data.Item2.Select(x => x.Item1))
-                        .Add<Entity>(x => x.ID)));
-            }
-        }
-        var results = Client.QueryMultiple(queryDefs);
-        foreach(var (prop, data) in newData)
-        {
-            var table = results.GetOrDefault(prop.Name);
-            if(table is null)
-            {
-                continue;
-            }
-            foreach (var entity in data.Item3)
-            {
-                var linkID = (Guid)prop.Getter()(entity);
-                var row = table.Rows.FirstOrDefault(x => x.Get<Entity, Guid>(x => x.ID) == linkID);
-                if (row is not null)
-                {
-                    foreach (var (name, property) in data.Item2)
-                    {
-                        if (!entity.LoadedColumns.Contains(property.Name))
-                        {
-                            property.Setter()(entity, row[name]);
-                            entity.LoadedColumns.Add(property.Name);
-                        }
-                    }
-                }
-            }
-        }
+        grid.Items.LoadForeignProperties(columns);
     }
 
     public static void RequireColumns<T>(this IDynamicMemoryEntityGrid<T> grid, Columns<T> columns)