Example #1
0
 public DomainPropSetBase(DomainObject objParent, XPropInfoObject xpropInfo)
     : base(objParent, xpropInfo)
 {
     if (m_objParent.IsNew)
     {
         // свойство нового объекта всегда "прогружено"
         m_state = DomainPropLoadableState.Loaded;
     }
     else
     {
         m_state = DomainPropLoadableState.Ghost;
     }
 }
Example #2
0
        /// <summary>
        /// Рекурсивно обходит дерево объектов и добавляет их в множество objSet
        /// </summary>
        /// <param name="xmlObject">текущий xml-объект</param>
        /// <param name="bIsRoot">Признак корневого объекта в пакете (x-datagram)</param>
        private void walkThroughXmlObjects(XmlElement xmlObject, bool bIsRoot)
        {
            string     sParentTypeName;                 // наименование типа родительского объекта
            string     sParentPropName;                 // наименование родительского свойства
            XmlElement xmlProp;

            XTypeInfo typeInfo = m_xmodel.GetTypeByName(xmlObject.LocalName);

            if (!typeInfo.IsTemporary)
            {
                // если у объекта нет атрибута oid, то установим его, сгенерировав новый гуид
                if (!xmlObject.HasAttribute("oid"))
                {
                    xmlObject.SetAttribute("oid", XmlConvert.ToString(Guid.NewGuid()));
                }

                if (!bIsRoot)
                {
                    Debug.Assert(xmlObject.ParentNode != null);
                    Debug.Assert(xmlObject.ParentNode.ParentNode != null);
                    // для вложенного объекта создадим обратное свойство, если его нет,
                    // или проверим, что в нем есть ссылка на родителя, если оно есть.
                    // Массивы и членство в массиве игнорируем.
                    // получим наименование типа родительского объекта
                    sParentTypeName = xmlObject.ParentNode.ParentNode.LocalName;
                    // получим наименование родительского свойства
                    sParentPropName = xmlObject.ParentNode.LocalName;
                    // получим метаданные родительского свойства
                    XPropInfoObject xpropParent = (XPropInfoObject)m_xmodel.GetTypeByName(sParentTypeName).GetProp(sParentPropName);
                    //if (xpropParent != null && xpropParent.Capacity != XPropCapacity.ArrayMembership && xpropParent.Capacity != XPropCapacity.Array)
                    if (xpropParent != null && (xpropParent.Capacity == XPropCapacity.Collection || xpropParent.Capacity == XPropCapacity.CollectionMembership))
                    {
                        if (xpropParent.ReverseProp != null)
                        {
                            XPropInfoObject xprop = (XPropInfoObject)xpropParent.ReverseProp;
                            // обратное свойство есть - поищем его в текущем объекте
                            xmlProp = (XmlElement)xmlObject.SelectSingleNode(xpropParent.ReverseProp.Name);

                            if (xmlProp == null)
                            {
                                // не нашли - надо создать и поместить туда заглушку родительского объекта
                                xmlProp = xmlObject.OwnerDocument.CreateElement(xprop.Name);
                                xmlProp.AppendChild(XStorageUtils.CreateStubFromObject((XmlElement)xmlObject.ParentNode.ParentNode));
                                xmlObject.AppendChild(xmlProp);
                            }
                            else if (xmlProp.GetAttribute("loaded").Length > 0 || !xmlProp.HasChildNodes && xmlObject.HasAttribute("new"))
                            {
                                // нашли, но оно помеченно как непрогруженное или пустое нового объекта - удалим его
                                xmlProp.ParentNode.RemoveChild(xmlProp);
                            }
                            else if (!xmlProp.HasChildNodes)
                            {
                                // нашли, но пустое - добавим заглушку родительского объекта
                                xmlProp.AppendChild(XStorageUtils.CreateStubFromObject((XmlElement)xmlObject.ParentNode.ParentNode));
                                xmlObject.AppendChild(xmlProp);
                            }
                            else
                            {
                                // нашли обратное свойство и оно не пустое- проверим, что в нем есть ссылка на родительский объект
                                // если ссылки нет, то добавим ее
                                string sParentObjectID = xmlObject.ParentNode.ParentNode.Attributes["oid"].Value;
                                if (xmlProp.SelectSingleNode(String.Format("{0}[@oid='{1}']", sParentTypeName, sParentObjectID)) == null)
                                {
                                    xmlProp.AppendChild(XStorageUtils.CreateStubFromObject((XmlElement)xmlObject.ParentNode.ParentNode));
                                }
                            }
                            // пометим свойство специальным атрибутом влияющим на процедуру слияния копий объекта
                            xmlProp.SetAttribute(XObject.MERGE_ACTION_WEAK, "1");
                        }
                    }
                }

                Add(xmlObject, typeInfo);
            }
            // по всем объектам (не заглушкам!) в объектных свойствах переданного xml-объекта
            foreach (XmlElement xmlChildObject in xmlObject.SelectNodes("*/*[*]"))
            {
                walkThroughXmlObjects(xmlChildObject, false);
            }
        }
Example #3
0
        private void checkReverseProps()
        {
            foreach (XStorageObjectBase xobj in m_objects)
            {
                if (xobj is XStorageObjectToSave)
                {
                    foreach (DictionaryEntry entry in ((XStorageObjectToSave)xobj).GetPropsByCapacity(XPropCapacity.CollectionMembership /*, XPropCapacity.Link, XPropCapacity.LinkScalar*/))
                    {
                        string sPropName = (string)entry.Key;
                        Guid[] valueOIDs = (Guid[])entry.Value;
                        if (valueOIDs.Length == 0)
                        {
                            continue;
                        }
                        XPropInfoObject propInfo = (XPropInfoObject)xobj.TypeInfo.GetProp(sPropName);
                        bool            bError   = false;
                        foreach (Guid valueOID in valueOIDs)
                        {
                            XStorageObjectBase xobjValue = (XStorageObjectBase)m_objectsDictionary[valueOID];
                            if (xobjValue == null)
                            {
                                continue;
                            }
                            // владелец обратного свойства есть в датаграмме

                            if (!xobjValue.Props.Contains(propInfo.ReverseProp.Name))
                            {
                                continue;
                            }
                            // владелец обратного свойства содержит это обратное свойство

                            object vValue = xobjValue.Props[propInfo.ReverseProp.Name];
                            Debug.Assert(vValue != null);
                            if (propInfo.ReverseProp is XPropInfoObjectScalar)
                            {
                                if (vValue == DBNull.Value || (Guid)vValue != xobj.ObjectID)
                                {
                                    bError = true;
                                }
                            }
                            else
                            {
                                // обратное свойство - коллекция, в свойстве массив гидов
                                Guid[] valueOIDsReverse = (Guid[])vValue;
                                // в этом массиве должна быть ссылка на текущий объект (xobj)
                                bool bFound = false;
                                foreach (Guid valueOIDReverse in valueOIDsReverse)
                                {
                                    if (valueOIDReverse == xobj.ObjectID)
                                    {
                                        bFound = true;
                                        break;
                                    }
                                }
                                if (!bFound)
                                {
                                    bError = true;
                                }
                            }
                            if (bError)
                            {
                                throw new XInvalidXmlForestException(
                                          String.Format("Не согласованы свойства объектов: {0}[ID='{1}'], свойство {2} и {3}[ID='{4}'], свойство {5}",
                                                        xobj.ObjectType,                                // 0
                                                        xobj.ObjectID,                                  // 1
                                                        sPropName,                                      // 2
                                                        xobjValue.ObjectType,                           // 3
                                                        xobjValue.ObjectID,                             // 4
                                                        propInfo.ReverseProp.Name                       // 5
                                                        )
                                          );
                            }
                        }
                    }
                }
            }
        }
Example #4
0
 public DomainPropObjectScalar(DomainObject obj, XPropInfoObject xpropInfo)
     : base(obj, xpropInfo)
 {
 }
        private void checkAndSyncReverseProps(DomainObjectDataSet dataSet)
        {
            IEnumerator enumerator = dataSet.GetModifiedObjectsEnumerator(true);
            object      vPropValue;

            while (enumerator.MoveNext())
            {
                DomainObjectData xobj = (DomainObjectData)enumerator.Current;
                foreach (string sPropName in xobj.UpdatedPropNames)
                {
                    XPropInfoBase propInfo = xobj.TypeInfo.GetProp(sPropName);
                    Debug.Assert(propInfo != null);
                    if (propInfo.VarType != XPropType.vt_object)
                    {
                        continue;
                    }

                    XPropInfoObject propInfoObj = (XPropInfoObject)propInfo;
                    if (propInfoObj.Capacity == XPropCapacity.CollectionMembership || propInfoObj.Capacity == XPropCapacity.Link)
                    {
                        Guid[] values = (Guid[])xobj.GetUpdatedPropValue(sPropName);
                        if (values.Length > 0)
                        {
                            foreach (Guid valueObjectID in values)
                            {
                                bool             bError    = false;
                                DomainObjectData xobjValue = dataSet.Find(propInfoObj.ReferedType.Name, valueObjectID);
                                // объект-значение объектного свойства sPropName есть в контексте и он будет сохраняться
                                if (xobjValue != null && xobjValue.HasNewData)
                                {
                                    // если текущее свойство "членство в коллекции" и в объекте-значении есть обратное свойство (коллекция),
                                    // проверим, что обратное свойство содержит ссылку на текущий объект (xobj)
                                    if (propInfoObj.Capacity == XPropCapacity.CollectionMembership && xobjValue.HasUpdatedProp(propInfoObj.ReverseProp.Name))
                                    {
                                        Guid[] propRevValues = (Guid[])xobjValue.GetUpdatedPropValue(propInfoObj.ReverseProp.Name);
                                        Debug.Assert(propRevValues != null);
                                        // если обратное свойство (коллекция) не содержит ссылку на текущий объект - исключение
                                        if (Array.IndexOf(propRevValues, xobj.ObjectID) == -1)
                                        {
                                            bError = true;
                                        }
                                    }
                                    // если текущее свойство линк, то установим обратное свойство - объектный скаляр
                                    else if (propInfoObj.Capacity == XPropCapacity.Link)
                                    {
                                        vPropValue = xobjValue.GetUpdatedPropValue(propInfoObj.ReverseProp.Name);
                                        // если обратное свойство (скаляр) установлено, то проверим, что оно ссылается на текущий объект
                                        if (vPropValue != null)
                                        {
                                            Debug.Assert(vPropValue is Guid);
                                            if ((vPropValue is DBNull) || ((Guid)vPropValue) != xobj.ObjectID)
                                            {
                                                bError = true;
                                            }
                                        }
                                        // обратное свойство неустановлено - установим его на текущий объект
                                        else
                                        {
                                            xobjValue.SetUpdatedPropValue(propInfoObj.ReverseProp.Name, xobj.ObjectID);
                                        }
                                    }
                                    if (bError)
                                    {
                                        throw new XInvalidXmlForestException(
                                                  String.Format("Не согласованы свойства объектов: {0}[ID='{1}'], свойство {2} и {3}[ID='{4}'], свойство {5}",
                                                                xobj.ObjectType,                                                // 0
                                                                xobj.ObjectID,                                                  // 1
                                                                sPropName,                                                      // 2
                                                                xobjValue.ObjectType,                                           // 3
                                                                xobjValue.ObjectID,                                             // 4
                                                                propInfoObj.ReverseProp.Name                                    // 5
                                                                ));
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Добавляет значение в объектное свойство.
        /// Если объект-значение присутствует в контексте, то запускаем его сериализацию (serializeObject), иначе создаем болванку (тип+идентификатор)
        /// </summary>
        /// <param name="dataSet"></param>
        /// <param name="xmlProp">Текущее объектное свойство (скалярное или массивное)</param>
        /// <param name="propInfo">Метаданные свойства</param>
        /// <param name="valueOID">Идентификатор объекта-значения</param>
        private void addValueIntoObjectProp(DomainObjectDataSet dataSet, XmlElement xmlProp, XPropInfoObject propInfo, Guid valueOID, PreloadsNavigator nav)
        {
            XmlElement       xmlObjectValue;                    // xml-представление объект-значение
            DomainObjectData xobjValue = null;                  // объект-значение свойства в контексте

            // теоретически контекста может не быть
            if (dataSet != null && nav != null)
            {
                xobjValue = dataSet.Find(propInfo.ReferedType.Name, valueOID);
            }
            if (xobjValue != null && nav != null)
            {
                // Объект-значение свойства загружен в контекст и задан навигатор - запустим рекурсивно его сериализацию
                xmlObjectValue = serializeObject(xobjValue, xmlProp.OwnerDocument, nav);
            }
            else
            {
                xmlObjectValue = xmlProp.OwnerDocument.CreateElement(propInfo.ReferedType.Name);
                xmlObjectValue.SetAttribute("oid", valueOID.ToString());
            }
            // добавим объект-значение (буть-то ссылка или полный объект)
            xmlProp.AppendChild(xmlObjectValue);
        }
 /// <summary>
 /// Записывает данные объектного массивного свойства
 /// </summary>
 /// <param name="xobjOwner">владелец свойства</param>
 /// <param name="xmlProp">xml-свойство</param>
 /// <param name="vPropValue">Значение массивного свойства (null или Guid[])</param>
 /// <param name="nav">навигатор по список прогружаемых свойств</param>
 private void writeArrayProp(DomainObjectData xobjOwner, XmlElement xmlProp, object vPropValue, XPropInfoObject propInfo, PreloadsNavigator nav)
 {
     if (vPropValue == null)
     {
         if (!xobjOwner.IsNew)
         {
             xmlProp.SetAttribute("loaded", "0");
         }
     }
     else
     {
         // свойство есть и загруженное
         Guid[] oids = (Guid[])vPropValue;
         foreach (Guid valueOID in oids)
         {
             addValueIntoObjectProp(xobjOwner.Context, xmlProp, propInfo, valueOID, nav);
         }
     }
 }
Example #8
0
        /// <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);
                    }
                }
            }
        }
Example #9
0
        /// <summary>
        /// Подчищает ссылки объектов, удаленных из линков
        /// </summary>
        /// <param name="xs">Реализация XStorageConnection</param>
        /// <param name="disp">диспетчер запросов</param>
        /// <param name="datagram">Множество обрабатываемых объектов</param>
        protected void purgeLinks(XStorageConnection xs, XDbStatementDispatcher disp, XDatagram datagram)
        {
            StringBuilder queryBuilder;                 // построитель запроса
            string        sTypeName;                    // наименование типа объекта владельца обратного свойства

            if (datagram.ObjectsToUpdate.Count == 0)
            {
                return;                                                 // обновлять нечего
            }
            queryBuilder = new StringBuilder();
            // по всем объектам из списка обновляемых
            foreach (XStorageObjectToSave xobj in datagram.ObjectsToUpdate)
            {
                foreach (DictionaryEntry entry in xobj.GetPropsByCapacity(XPropCapacity.Link, XPropCapacity.LinkScalar))
                {
                    XPropInfoObject propInfo = (XPropInfoObject)xobj.TypeInfo.GetProp((string)entry.Key);
                    Guid[]          values   = (Guid[])entry.Value;
                    queryBuilder.Length = 0;
                    sTypeName           = propInfo.ReferedType.Name;
                    // всем объектам ссылающимся на текущуй объект по обратному относительно текущего свойству
                    // обNULLим ссылку..
                    queryBuilder.AppendFormat(
                        "UPDATE {0} SET {1} = NULL, {2} = CASE WHEN {2}<{3} THEN {2}+1 ELSE 1 END {5}WHERE {1}={4} ",
                        xs.GetTableQName(xobj.TypeInfo.Schema, sTypeName),                                      // 0
                        xs.ArrangeSqlName(propInfo.ReverseProp.Name),                                           // 1
                        xs.ArrangeSqlName("ts"),                                                                // 2
                        Int64.MaxValue,                                                                         // 3
                        xs.ArrangeSqlGuid(xobj.ObjectID),                                                       // 4
                        xs.Behavior.SqlNewLine                                                                  // 5
                        );
                    // ...при условии, что идентификаторы этих объектов не упомянуты в свойстве
                    if (values.Length > 0)
                    {
                        // в текущем св-ве есть объекты (их идентификаторы в values)
                        queryBuilder.AppendFormat("AND NOT {0} IN (", xs.ArrangeSqlName("ObjectID"));
                        foreach (Guid value in values)
                        {
                            queryBuilder.AppendFormat("{0},", xs.ArrangeSqlGuid(value));
                        }
                        // отрежем последнюю запятую
                        queryBuilder.Length--;
                        queryBuilder.Append(") ");
                    }
                    // так же исключим объекты, присутствующие в списке удаляемых,
                    // т.к. при удалении проверяется ts, а формируемый update его увеличивает, да и вообще бессмыселен
                    if (datagram.ObjectsToDelete.Count > 0)
                    {
                        StringBuilder addWhereBuilder = new StringBuilder();
                        foreach (XStorageObjectToDelete xobjDel in datagram.ObjectsToDelete)
                        {
                            if (xobjDel.ObjectType == sTypeName)
                            {
                                addWhereBuilder.AppendFormat("{0},", xs.ArrangeSqlGuid(xobjDel.ObjectID));
                            }
                        }
                        if (addWhereBuilder.Length > 0)
                        {
                            // отрежем последнюю запятую
                            addWhereBuilder.Length--;
                            queryBuilder.AppendFormat("AND NOT {0} IN ({1}) ",
                                                      xs.ArrangeSqlName("ObjectID"),            // 0
                                                      addWhereBuilder.ToString()                // 1
                                                      );
                        }
                    }
                    disp.DispatchStatement(queryBuilder.ToString(), false);
                }               // конец цикла по свойствам объекта xobj
            }                   // конец цикла по объектам из списка обновляемых
        }