private void ForceFoliation(XmlBoundElement node, ElementState newState) { lock (_foliationLock) { if (node.ElementState != ElementState.Defoliated) // The region was foliated by an other thread while this thread was locked return; // Node must be a row-elem associated w/ a non-deleted row Debug.Assert(node.Row != null); Debug.Assert(node.Row.RowState != DataRowState.Deleted); node.ElementState = ElementState.Foliating; bool saveIgnore = IgnoreXmlEvents; IgnoreXmlEvents = true; try { XmlNode priorNode = null; DataRow row = node.Row; // create new attrs & elements for row // For detached rows: we are in sync w/ temp values // For non-detached rows: we are in sync w/ the current values // For deleted rows: we never sync DataRowVersion rowVersion = (row.RowState == DataRowState.Detached) ? DataRowVersion.Proposed : DataRowVersion.Current; foreach (DataColumn col in row.Table.Columns) { if (!IsNotMapped(col)) { object value = row[col, rowVersion]; if (!Convert.IsDBNull(value)) { if (col.ColumnMapping == MappingType.Attribute) { node.SetAttribute(col.EncodedColumnName, col.Namespace, col.ConvertObjectToXml(value)); } else { XmlNode newNode = null; if (col.ColumnMapping == MappingType.Element) { newNode = new XmlBoundElement(string.Empty, col.EncodedColumnName, col.Namespace, this); newNode.AppendChild(CreateTextNode(col.ConvertObjectToXml(value))); if (priorNode != null) { node.InsertAfter(newNode, priorNode); } else if (node.FirstChild != null) { node.InsertBefore(newNode, node.FirstChild); } else { node.AppendChild(newNode); } priorNode = newNode; } else { Debug.Assert(col.ColumnMapping == MappingType.SimpleContent); newNode = CreateTextNode(col.ConvertObjectToXml(value)); if (node.FirstChild != null) node.InsertBefore(newNode, node.FirstChild); else node.AppendChild(newNode); if (priorNode == null) priorNode = newNode; } } } else { if (col.ColumnMapping == MappingType.SimpleContent) { XmlAttribute attr = CreateAttribute(XSI, Keywords.XSI_NIL, Keywords.XSINS); attr.Value = Keywords.TRUE; node.SetAttributeNode(attr); _bHasXSINIL = true; } } } } } finally { IgnoreXmlEvents = saveIgnore; node.ElementState = newState; } // update all live pointers OnFoliated(node); } }
private void OnColumnValueChanged(DataRow row, DataColumn col, XmlBoundElement rowElement) { if (IsNotMapped(col)) { goto lblDoNestedRelationSync; } object value = row[col]; if (col.ColumnMapping == MappingType.SimpleContent && Convert.IsDBNull(value) && !rowElement.IsFoliated) { ForceFoliation(rowElement, ElementState.WeakFoliation); } else { // no need to sync if not foliated if (!IsFoliated(rowElement)) { #if DEBUG // If the new value is null, we should be already foliated if there is a DataPointer that points to the column // (see OnRowChanging, case DataRowAction.Change) if (Convert.IsDBNull(row[col, DataRowVersion.Current])) { try { if (_pointers.Count > 0) { object pointer = null; foreach (DictionaryEntry entry in _pointers) { pointer = entry.Value; Debug.Assert((pointer != null) && !((IXmlDataVirtualNode)pointer).IsOnColumn(col)); } } } catch (Exception e) when (Data.Common.ADP.IsCatchableExceptionType(e)) { // We may get an exception if we are in foreach and a new pointer has been added to this.pointers. When this happens, we will skip this check and ignore the exceptions } } #endif goto lblDoNestedRelationSync; } } if (IsTextOnly(col)) { if (Convert.IsDBNull(value)) { value = string.Empty; //make sure that rowElement has Attribute xsi:nil and its value is true XmlAttribute attr = rowElement.GetAttributeNode(XSI_NIL); if (attr == null) { attr = CreateAttribute(XSI, Keywords.XSI_NIL, Keywords.XSINS); attr.Value = Keywords.TRUE; rowElement.SetAttributeNode(attr); _bHasXSINIL = true; } else attr.Value = Keywords.TRUE; } else { //make sure that if rowElement has Attribute xsi:nil, its value is false XmlAttribute attr = rowElement.GetAttributeNode(XSI_NIL); if (attr != null) attr.Value = Keywords.FALSE; } ReplaceInitialChildText(rowElement, col.ConvertObjectToXml(value)); goto lblDoNestedRelationSync; } // update the attribute that maps to the column bool fFound = false; // Find the field node and set it's value if (col.ColumnMapping == MappingType.Attribute) { foreach (XmlAttribute attr in rowElement.Attributes) { if (attr.LocalName == col.EncodedColumnName && attr.NamespaceURI == col.Namespace) { if (Convert.IsDBNull(value)) { attr.OwnerElement.Attributes.Remove(attr); } else { attr.Value = col.ConvertObjectToXml(value); } fFound = true; break; } } // create new attribute if we didn't find one. if (!fFound && !Convert.IsDBNull(value)) { rowElement.SetAttribute(col.EncodedColumnName, col.Namespace, col.ConvertObjectToXml(value)); } } else { // update elements that map to the column... RegionIterator iter = new RegionIterator(rowElement); bool fMore = iter.Next(); while (fMore) { if (iter.CurrentNode.NodeType == XmlNodeType.Element) { XmlElement e = (XmlElement)iter.CurrentNode; Debug.Assert(e != null); //we should skip the subregion XmlBoundElement be = e as XmlBoundElement; if (be != null && be.Row != null) { fMore = iter.NextRight(); //skip over the sub-region continue; } if (e.LocalName == col.EncodedColumnName && e.NamespaceURI == col.Namespace) { fFound = true; if (Convert.IsDBNull(value)) { PromoteNonValueChildren(e); fMore = iter.NextRight(); e.ParentNode.RemoveChild(e); // keep looking for more matching elements continue; } else { ReplaceInitialChildText(e, col.ConvertObjectToXml(value)); //make sure that if the Element has Attribute xsi:nil, its value is false XmlAttribute attr = e.GetAttributeNode(XSI_NIL); if (attr != null) attr.Value = Keywords.FALSE; // no need to look any further. goto lblDoNestedRelationSync; } } } fMore = iter.Next(); } // create new element if we didn't find one. if (!fFound && !Convert.IsDBNull(value)) { XmlElement newElem = new XmlBoundElement(string.Empty, col.EncodedColumnName, col.Namespace, this); newElem.AppendChild(CreateTextNode(col.ConvertObjectToXml(value))); XmlNode elemBefore = GetColumnInsertAfterLocation(row, col, rowElement); if (elemBefore != null) { rowElement.InsertAfter(newElem, elemBefore); } else if (rowElement.FirstChild != null) { rowElement.InsertBefore(newElem, rowElement.FirstChild); } else { rowElement.AppendChild(newElem); } } } lblDoNestedRelationSync: // Change the XML to conform to the (potentially) change in parent nested relation DataRelation relation = GetNestedParentRelation(row); if (relation != null) { Debug.Assert(relation.ChildTable == row.Table); if (relation.ChildKey.ContainsColumn(col)) OnNestedParentChange(row, rowElement, col); } }