Exemple #1
0
        /// <summary>
        /// copy row from node (a json object) to target (a daton root or child row), based on tableDef
        /// </summary>
        private static void ReadCompatibleJsonRow(JObject node, TableDef tableDef, Row targetRow)
        {
            //copy fields in this row
            foreach (var colDef in tableDef.Cols)
            {
                //get value from json or skip
                var jtoken = node.GetValue(colDef.Name, StringComparison.OrdinalIgnoreCase);
                if (jtoken == null)
                {
                    continue;
                }
                var value = ParseNode(jtoken, colDef.CSType);

                //write to row
                if (colDef.IsCustom)
                {
                    targetRow.SetCustom(colDef.Name, value);
                }
                else
                {
                    var targetField = tableDef.RowType.GetField(colDef.Name);
                    if (targetField == null)
                    {
                        throw new Exception($"Expected {colDef.Name} to be a member of {tableDef.RowType.Name}");
                    }
                    targetField.SetValue(targetRow, value);
                }
            }

            //recursively copy child rows
            if (tableDef.Children != null)
            {
                foreach (var childTableDef in tableDef.Children)
                {
                    //get json node or skip
                    var jtoken = node.GetValue(childTableDef.Name, StringComparison.OrdinalIgnoreCase);
                    if (!(jtoken is JArray jarray))
                    {
                        continue;
                    }

                    //get target object or skip
                    var targetListField = tableDef.RowType.GetField(childTableDef.Name);
                    if (targetListField == null)
                    {
                        continue;
                    }
                    var listType = targetListField.FieldType;
                    if (!(targetListField is IList) || !listType.IsGenericType)
                    {
                        continue;                                                         //is not List<xxx>
                    }
                    var list = Utils.CreateOrGetFieldValue <IList>(targetRow, targetListField);

                    //loop rows
                    foreach (var node2 in jarray)
                    {
                        if (!(node2 is JObject node3))
                        {
                            throw new Exception("Array elements must be JSON objects");
                        }
                        var row = Utils.ConstructRow(childTableDef.RowType, childTableDef);
                        list.Add(row);
                        ReadCompatibleJsonRow(node3, childTableDef, row);
                    }
                }
            }
        }
Exemple #2
0
        private static void ReadJsonDiffRow(JObject node, PersistonDiff.DiffRow target, TableDef tableDef, bool allowChildren)
        {
            //copy fields in this row
            foreach (var colDef in tableDef.Cols)
            {
                //get json value or skip
                var jtoken = node.GetValue(colDef.Name, StringComparison.OrdinalIgnoreCase);
                if (jtoken == null)
                {
                    continue;
                }

                //copy
                var value = ParseNode(jtoken, colDef.CSType);
                target.Columns[colDef.Name] = value;
            }

            //recursively copy child rows
            if (tableDef.Children != null && allowChildren)
            {
                foreach (var childTableDef in tableDef.Children)
                {
                    if (target.ChildTables == null)
                    {
                        target.ChildTables = new Dictionary <TableDef, List <PersistonDiff.DiffRow> >();
                    }
                    if (!target.ChildTables.ContainsKey(childTableDef))
                    {
                        target.ChildTables[childTableDef] = new List <PersistonDiff.DiffRow>();
                    }
                    ReadJsonDiffRowArray(node, childTableDef, target.ChildTables[childTableDef]);
                }
            }
        }
Exemple #3
0
        /// <summary>
        /// Called from Load after loading the top table to load all other tables.
        /// The base implementation is careful to only load a table once regardless of the number of detail levels and the number of parent/grandparent
        /// rows. For example for a persiston whose table structure is (Sale, LineItem, LineItemNote), then there will only be one select statement
        /// issued for LineItemNote, using an IN clause for the primary keys of LineItem (which were already loaded).
        /// (The call syntax would be complex to invoke this for the top table so this is called only once for the children of the top
        /// table.)
        /// </summary>
        /// <param name="parentRows">parent Row objects indexed by primary key value</param>
        protected async Task LoadChildTablesRecursive(Dictionary <object, Row> parentRows, IDbConnection db, DataDictionary dbdef, TableDef parentdef)
        {
            if (!parentRows.Any())
            {
                return;
            }
            if (parentdef.Children == null)
            {
                return;
            }

            //get parent keys in the form "1,2,3..."
            string parentKeyListFormatted = SqlSelectBuilder.FormatInClauseList(parentRows.Keys);

            foreach (var childTabledef in parentdef.Children)
            {
                //get rows - where clause is "parentkey in (...)"
                var whereClause = new SqlSelectBuilder.Where();
                whereClause.AddWhere($"{childTabledef.ParentKeyColumnName} in({parentKeyListFormatted})");
                var loadResult = await LoadTable(db, dbdef, childTabledef, whereClause, childTabledef.DefaulSortColName, 0, 0);

                var rowdict = loadResult.RowsByParentKey;

                //deal out the rows into the parent objects' Lists of this type
                var listField = parentdef.RowType.GetField(childTabledef.Name);
                foreach (object parentKey in rowdict.Keys)
                {
                    var rowsForParent = rowdict[parentKey];
                    var parentRow     = parentRows[parentKey];
                    var list          = Utils.CreateOrGetFieldValue <IList>(parentRow, listField);
                    foreach (var row in rowsForParent)
                    {
                        list.Add(row);
                    }
                }

                //recur
                var rowsByPK = RestructureByPrimaryKey(childTabledef, rowdict);
                await LoadChildTablesRecursive(rowsByPK, db, dbdef, childTabledef);
            }
        }
Exemple #4
0
        /// <summary>
        /// Given a single row of changes (add/new/delete) and a list, either delete or update the list row with the matching primary key,
        /// or add a new row.
        /// </summary>
        /// <returns>true if any actual changes</returns>
        private bool ApplyDiffRowToList(TableDef tabledef, DiffRow source, IList targetList)
        {
            bool anyChanges = false;

            if (tabledef.PrimaryKeyColName == null)
            {
                throw new Exception("Primary key not defined");
            }
            var itemType = targetList.GetType().GenericTypeArguments[0];
            var pkField  = itemType.GetField(tabledef.PrimaryKeyColName);

            if (pkField == null)
            {
                throw new Exception($"Primary key field not found in type {itemType.Name}");
            }

            if (source.Kind == DiffKind.DeletedRow)
            {
                if (!source.Columns.TryGetValue(tabledef.PrimaryKeyColName, out object pkToDelete))
                {
                    throw new Exception("Deleted row in diff needs primary key member");
                }
                int idxToDelete = Utils.IndexOfPrimaryKeyMatch(targetList, pkField, pkToDelete);
                if (idxToDelete >= 0)
                {
                    targetList.RemoveAt(idxToDelete);
                    anyChanges = true;
                }
            }

            else //new or update
            {
                //find or create row
                Row target;
                if (source.Kind == DiffKind.NewRow)
                {
                    target = Utils.Construct(itemType) as Row;
                    targetList.Add(target);
                    anyChanges = true;
                }
                else
                {
                    if (!source.Columns.TryGetValue(tabledef.PrimaryKeyColName, out object pkToUpdate))
                    {
                        throw new Exception("Updated row in diff needs primary key member");
                    }
                    int idxToUpdate = Utils.IndexOfPrimaryKeyMatch(targetList, pkField, pkToUpdate);
                    if (idxToUpdate < 0)
                    {
                        throw new Exception("Row to update is not found");
                    }
                    target = targetList[idxToUpdate] as Row;
                }

                //copy values
                foreach (string colName in source.Columns.Keys)
                {
                    anyChanges |= SetValue(tabledef, source, colName, target);
                }

                //process child tables
                anyChanges |= ApplyChildTables(source, target, itemType);
            }
            return(anyChanges);
        }
Exemple #5
0
        /// <summary>
        /// Load rows given by where clause for a table and return them as dictionary indexed by the parent key value.
        /// If there is no parent key for the table, then the dictionary will have one entry with key="".
        /// The returned list members are objects of the type indicated by tabledef.
        /// The implementation loads one additional row to determine whether the load was complete, if pageSize is nonzero.
        /// </summary>
        /// <param name="pageSize">if zero, does not do paging</param>
        /// <param name="whereClause">can be null</param>
        protected virtual Task <SingleLoadResult> LoadTable(IDbConnection db, DataDictionary dbdef, TableDef tabledef, SqlSelectBuilder.Where whereClause,
                                                            string sortColName, int pageSize, int pageNo)
        {
            var    colInfos          = BuildColumnsToLoadList(dbdef, tabledef);
            string parentKeyColName  = tabledef.ParentKeyColumnName;
            int    parentKeyColIndex = -1;
            var    columnNames       = colInfos.Select(c => c.SqlExpression).ToList();

            if (parentKeyColName != null)
            {
                parentKeyColIndex = columnNames.Count;
                columnNames.Add(parentKeyColName);
            }
            int customColIndex = -1;

            if (tabledef.HasCustomColumns)
            {
                customColIndex = columnNames.Count;
                columnNames.Add(CUSTOMCOLNAME);
            }
            var sql = new SqlSelectBuilder(SqlFlavor, tabledef.SqlTableName, sortColName, columnNames)
            {
                PageSize = pageSize,
                PageNo   = pageNo
            };

            if (whereClause != null)
            {
                sql.WhereClause = whereClause;
            }
            var  rowsByParentKey = new Dictionary <object, List <Row> >();
            bool isComplete      = true;

            using (var cmd = db.CreateCommand())
            {
                cmd.CommandText = CustomizeSqlStatement(sql.ToString());
                if (whereClause != null)
                {
                    whereClause.ExportParameters(cmd);
                }
                using (var reader = cmd.ExecuteReader())
                {
                    int rowsLoaded = 0;
                    while (reader.Read())
                    {
                        //if this is the throw-away row that was one more than the page size, then note as incomplete but don't look at it
                        if (++rowsLoaded > pageSize && pageSize > 0)
                        {
                            isComplete = false;
                            break;
                        }

                        //read parent key value
                        object pk = "";
                        if (parentKeyColIndex >= 0)
                        {
                            pk = reader.GetValue(parentKeyColIndex);
                        }

                        //read remaining values
                        var row = Utils.Construct(tabledef.RowType) as Row;
                        SetRowFromReader(colInfos, reader, row);

                        //read custom values
                        if (tabledef.HasCustomColumns)
                        {
                            string json = (string)Utils.ChangeType(reader.GetValue(customColIndex), typeof(string));
                            if (!string.IsNullOrEmpty(json))
                            {
                                SetRowFromCustomValues(json, tabledef, row);
                            }
                        }

                        //store in return dict
                        if (!rowsByParentKey.ContainsKey(pk))
                        {
                            rowsByParentKey[pk] = new List <Row>();
                        }
                        rowsByParentKey[pk].Add(row);
                    }
                }
            }
            var ret = new SingleLoadResult
            {
                RowsByParentKey = rowsByParentKey,
                IsComplete      = isComplete
            };

            return(Task.FromResult(ret));
        }