/// <summary> /// Convert an Erlang hierarchical term representing a schema to a Row. /// </summary> /// <param name="row">Row to update</param> /// <param name="data"> /// Data to update row with. /// The data must be in the form {SchemaName::atom, [{FieldName::atom(), Value}]}. /// </param> /// <param name="schema">Alternative schema to use in place of row.Schema</param> /// <param name="targetName">Name of the target for looking up field attributes</param> /// <param name="schemaName">Alternative name of the top-most 'SchemaName' atom used in the "data".</param> /// <param name="knownSchemas">List of known schemas to use when initializing a field a DynamicRow type.</param> public static void Update(this Row row, IErlObject data, Schema schema = null, string targetName = null, string schemaName = null, Registry <Schema> knownSchemas = null) { if (schema == null) { schema = row.Schema; } if (schemaName == null) { schemaName = schema.Name; } if (data == null) { data = new ErlTuple(new ErlAtom(schemaName), new ErlList()); } // Input data must be in the form: {SchemaName::atom(), [{FieldName::atom(), Value}]} // where Value can be any primitive value or a hierarchical value with another Row type. if (!checkKeyValueTuple(data) || ((ErlTuple)data)[1].TypeOrder != ErlTypeOrder.ErlList) { throw new ErlDataAccessException( StringConsts.ERL_DS_CRUD_RESP_SCH_MISMATCH_ERROR.Args(data.ToString(), schema.Name)); } var dataList = ((ErlTuple)data)[1] as ErlList; // Make sure that the first element of the tuple matches the schema name if (!((ErlTuple)data)[0].ValueAsString.Equals(schemaName)) { throw new ErlDataAccessException( StringConsts.ERL_DS_CRUD_RESP_SCH_MISMATCH_ERROR.Args(data.ToString(), schema.Name)); } // Contains a set of field names that are present in configuration var presentFieldNames = new HashSet <string>(); foreach (var item in dataList.Where(checkKeyValueTuple).Cast <ErlTuple>()) { presentFieldNames.Add(item[0].ValueAsString); } ErlList newList = null; foreach (var fld in schema.Where(fd => typeof(Row).IsAssignableFrom(fd.Type))) { if (!presentFieldNames.Contains(fld.Name)) { if (newList == null) { newList = (ErlList)dataList.Clone(); } // Add: {FieldName::atom(), []} newList.Add(new ErlTuple(new ErlAtom(fld.Name), new ErlList())); } } // If no new items were added to the list use current list: if (newList == null) { newList = dataList; } foreach (var item in newList.Where(checkKeyValueTuple).Cast <ErlTuple>()) { var name = item[0].ValueAsString; var value = item[1]; var fdef = schema[name]; var attr = fdef[targetName]; if (!attr.Visible || (attr.Metadata != null && attr.Metadata.Navigate("$readonly|$read-only|$read_only").ValueAsBool())) { continue; } // If this field is defined in the schema as a Row type, then we need to descend into the // value's hierarchical structure and treat it as a nested row if (typeof(Row).IsAssignableFrom(fdef.Type)) { // Get the row associated Schema chldSch; var chldRow = row[fdef.Order] as Row; // If the row has a field of Row type initialized, use its Schema value. if (chldRow != null) { chldSch = chldRow.Schema; } else { // Otherwise lookup the schema from the given registry if (!knownSchemas.TryGetValue(name, out chldSch)) { throw new ErlDataAccessException( StringConsts.ERL_DS_SCHEMA_NOT_KNOWN_ERROR.Args(name, data.ToString())); } // Construct the field's value as dynmiac row of the looked up schema type chldRow = new DynamicRow(chldSch); chldRow.ApplyDefaultFieldValues(); row[fdef.Order] = chldRow; } if (value.TypeOrder != ErlTypeOrder.ErlList) { throw new ErlDataAccessException( StringConsts.ERL_DS_SCHEMA_INVALID_VALUE_ERROR.Args(chldSch.Name, value)); } // Recursively update the field's value from given data by using the field's schema: chldRow.Update(item, chldSch, targetName, knownSchemas: knownSchemas); } else { // This is a primitive value type var clr = SchemaMap.ErlToClrValue(value, schema, fdef, null, data); row.SetFieldValue(fdef, clr); } } }
public void ErlInsertManyDeleteAndQuery() { var schema = store.GetSchema(new Query("CRUD.SECDEF")); for (var i = 0; i < CCY_PAIRS.Length; i++) { var row = new DynamicRow(schema); row.ApplyDefaultFieldValues(store.TargetName); var ccy1 = CCY_PAIRS[i].Substring(0, 3); var ccy2 = CCY_PAIRS[i].Substring(4, 3); row["xchg"] = "NYSE"; row["symbol"] = ccy1 + ccy2; row["instr"] = CCY_PAIRS[i]; row["secid"] = i; row["xchg_secid"] = 1000 + i; row["ccy"] = ccy1; row["settl_ccy"] = ccy2; row["contr_mult"] = 1.0d; row["px_step"] = 10e-5d; Aver.IsNull(row.Validate()); var affected = store.Upsert(row); Aver.AreEqual(1, affected); } var qry = new Query("CRUD.SecDef.ByExchange") { new Query.Param("Exchange", "NYSE") }; var data = store.LoadOneRowset(qry); Aver.IsNotNull(data); Console.WriteLine(data.ToJSON(JSONWritingOptions.PrettyPrintRowsAsMap)); Aver.AreEqual(CCY_PAIRS.Length, data.Count); var del = new DynamicRow(schema); del["xchg"] = "CLE"; del["symbol"] = "USDMXN"; Aver.AreEqual(1, store.Delete(del)); data = store.LoadOneRowset(qry);//NYSE Aver.IsNotNull(data); Aver.AreEqual(CCY_PAIRS.Length, data.Count); qry = new Query("CRUD.SecDef.ByExchange") { new Query.Param("Exchange", "CLE") }; data = store.LoadOneRowset(qry);//Requery for CLE Aver.IsNotNull(data); Aver.AreEqual(CCY_PAIRS.Length - 1, data.Count);//1 was deleted!!!!!!!!!!! }