private void createObjectsFromLinks(XStorageObjectBase xobj) { // TODO: Здесь вопрос: удаленные объекты могут содержать линки с ссылками и, если да, то надо ли их обрабатывать ? if (xobj is XStorageObjectToDelete) { return; } XStorageObjectToSave xobjSave = (XStorageObjectToSave)xobj; foreach (DictionaryEntry entry in xobjSave.GetPropsByCapacity(XPropCapacity.Link, XPropCapacity.LinkScalar)) { Guid[] valueOIDs = (Guid[])entry.Value; if (valueOIDs.Length == 0) { continue; } string sPropName = (string)entry.Key; XPropInfoObjectLink propInfo = (XPropInfoObjectLink)xobj.TypeInfo.GetProp(sPropName); int nIndex = 0; foreach (Guid valueOID in valueOIDs) { XStorageObjectBase xobjValue = new XStorageObjectToSave(propInfo.ReferedType, valueOID, -1, false); xobjValue.Props[propInfo.ReverseProp.Name] = xobj.ObjectID; // пометим свойство специальным атрибутом, чтобы для него не выполнялась проверка на совпадение содержимого при мердже xobjValue.SetPropMergeMode(propInfo.ReverseProp.Name, XStorageObjectPropMergeModes.Weak); xobjValue = add(xobjValue); // упорядоченный линк ? - установим индексное свойство if (propInfo.OrderByProp != null) { xobjValue.Props[propInfo.OrderByProp.Name] = nIndex++; // пометим свойство специальным атрибутом, говорящим о том что при Merge'е данное значение затрет другое значение xobjValue.SetPropMergeMode(propInfo.OrderByProp.Name, XStorageObjectPropMergeModes.Replace); } } } }
private ArrayList normalizeObject(XStorageObjectBase xobj) { if (xobj is XStorageObjectToDelete) { return(null); } XStorageObjectToSave xobjSave = (XStorageObjectToSave)xobj; ArrayList aNewObjects = null; foreach (DictionaryEntry entry in xobjSave.GetPropsByCapacity(XPropCapacity.Link, XPropCapacity.LinkScalar)) { Guid[] valueOIDs = (Guid[])entry.Value; if (valueOIDs.Length == 0) { continue; } string sPropName = (string)entry.Key; XPropInfoObjectLink propInfo = (XPropInfoObjectLink)xobj.TypeInfo.GetProp(sPropName); int nIndex = 0; foreach (Guid valueOID in valueOIDs) { XStorageObjectBase xobjValue = (XStorageObjectBase)m_objectsDictionary[valueOID]; if (xobjValue == null) { // в датаграмме нет объекта, на который установлена ссылка в линке - значит создадим, этот объект точно не новый xobjValue = new XStorageObjectToSave(propInfo.ReferedType, valueOID, -1, false); if (aNewObjects == null) { aNewObjects = new ArrayList(); } aNewObjects.Add(xobjValue); // на случай если данный объект содержится еще в каком-нибудь линке m_objectsDictionary.Add(valueOID, xobjValue); } else { object vValue = xobjValue.Props[propInfo.ReverseProp.Name]; if (vValue == null || vValue == DBNull.Value) { // все хорошо - обратное свойство (объектный скаляр) пустое - установим его на текущий объект (xobj) xobjValue.Props[propInfo.ReverseProp.Name] = xobj.ObjectID; } else { Debug.Assert(vValue is Guid); // больше ничего другого быть не должно! // обратное свойство уже заполнено, проверим что оно ссылается на текущий объект. если это не так - ругаемся if (((Guid)vValue) != xobj.ObjectID) { throw new XInvalidXmlForestException("Ошибка при установке свойства " + propInfo.ReverseProp.Name + " объекта " + xobjValue.TypeInfo.Name + " [" + xobjValue.ObjectID + "]: нарушение согласованности со свойством " + sPropName + " объекта " + xobj.TypeInfo.Name + " [" + xobj.ObjectID + "]"); } } } // упорядоченный линк ? - установим индексное свойство if (propInfo.OrderByProp != null) { xobjValue.Props[propInfo.OrderByProp.Name] = nIndex++; } } } return(aNewObjects); }
/// <summary> /// Обновляет кросс-таблицы для массивных свойств (collection, collection-membership, array) заданного объекта /// </summary> /// <param name="xs">Реализация XStorageConnection</param> /// <param name="disp">диспетчер запросов</param> /// <param name="xobj">ds-Объект</param> protected void updateCrossTablesForObject(XStorageConnection xs, XDbStatementDispatcher disp, XStorageObjectToSave xobj) { string sPropName; // наименование свойства string sDBCrossTableName; // полное наименование кросс-таблицы int nIndex; // значение колонки k string sKeyColumn; // наименование колонки кросс таблицы, по которой будем очищать string sValueColumn; // // по всем свойствам текущего объекта вида: массив, коллекция, членство в коллекции: foreach (DictionaryEntry entry in xobj.GetPropsByCapacity(XPropCapacity.Collection, XPropCapacity.Array, XPropCapacity.CollectionMembership)) { sPropName = (string)entry.Key; XPropInfoObject propInfo = (XPropInfoObject)xobj.TypeInfo.GetProp(sPropName); Debug.Assert(entry.Value is Guid[]); Guid[] values = (Guid[])entry.Value; // сформируем наименование кросс-таблицы по поля, по которому будем очищать кросс-таблицу: sDBCrossTableName = xs.GetTableQName(xobj.SchemaName, xobj.TypeInfo.GetPropCrossTableName(sPropName)); // Сохранение массива: очистим по ObjectID и вставим все значения из свойства if (propInfo.Capacity == XPropCapacity.Array) { StringBuilder cmdBuilder = new StringBuilder(); // если сохраняем массив (array) нового объекта (отложенное обновление), то DELETE выполнять не будем if (!xobj.IsToInsert) { // сформируем необходимый оператор delete на кросс-таблицу: cmdBuilder.AppendFormat( "DELETE FROM {0} WHERE {1}={2}", sDBCrossTableName, // 0 xs.ArrangeSqlName("ObjectID"), // 1 xs.GetParameterName("ObjectID") // 2 ); disp.DispatchStatement( cmdBuilder.ToString(), new XDbParameter[] { xs.CreateCommand().CreateParameter("ObjectID", DbType.Guid, ParameterDirection.Input, false, xobj.ObjectID) }, false ); } nIndex = 0; // для каждого значения массивного свойства добавим INSERT в кросс-таблицу foreach (Guid value in values) { cmdBuilder.Length = 0; cmdBuilder.AppendFormat("INSERT INTO {0} ({1}, {2}, {3}) values ({4}, {5}, {6})", sDBCrossTableName, // 0 xs.ArrangeSqlName("ObjectID"), // 1 xs.ArrangeSqlName("Value"), // 2 xs.ArrangeSqlName("k"), // 3 xs.ArrangeSqlGuid(xobj.ObjectID), // 4 xs.ArrangeSqlGuid(value), // 5 nIndex // 6 ); disp.DispatchStatement(cmdBuilder.ToString(), false); ++nIndex; } } // Сохранение коллекции и членства в коллекции else { if (propInfo.Capacity == XPropCapacity.CollectionMembership) { sKeyColumn = "Value"; sValueColumn = "ObjectID"; } else { sKeyColumn = "ObjectID"; sValueColumn = "Value"; } // сформируем необходимый оператор delete на кросс-таблицу: StringBuilder cmdBuilder = new StringBuilder(); cmdBuilder.AppendFormat( "DELETE FROM {0} WHERE {1}={2}", sDBCrossTableName, // 0 xs.ArrangeSqlName(sKeyColumn), // 1 xs.ArrangeSqlGuid(xobj.ObjectID) // 2 //xs.GetParameterName("ObjectID") // 2 ); // если есть новые значения свойства, то исключим их из удаления if (values.Length > 0) { cmdBuilder.AppendFormat( " AND NOT {0} IN (", xs.ArrangeSqlName(sValueColumn) ); foreach (Guid oid in values) { cmdBuilder.Append(xs.ArrangeSqlGuid(oid)); cmdBuilder.Append(","); } // удалим последнюю запятую cmdBuilder.Length--; cmdBuilder.Append(")"); } disp.DispatchStatement( cmdBuilder.ToString(), //new XDbParameter[] {xs.CreateCommand().CreateParameter("ObjectID", DbType.Guid, ParameterDirection.Input, false, xobj.ObjectID)}, false ); // для каждого значения массивного свойства добавим INSERT в кросс-таблицу foreach (Guid value in values) { cmdBuilder.Length = 0; cmdBuilder.AppendFormat("INSERT INTO {0} ({1}, {2}) SELECT {3}, {4} WHERE NOT EXISTS (SELECT 1 FROM {0} WHERE {1}={3} AND {2}={4})", sDBCrossTableName, // 0 xs.ArrangeSqlName(sKeyColumn), // 1 xs.ArrangeSqlName(sValueColumn), // 2 xs.ArrangeSqlGuid(xobj.ObjectID), // 3 xs.ArrangeSqlGuid(value) // 4 ); disp.DispatchStatement(cmdBuilder.ToString(), false); } } } }