Пример #1
0
        private void GenerateBidirectionalCollectionChangeWorkUnits(AuditSync verSync, AbstractCollectionEvent evt,
                                                                    PersistentCollectionChangeWorkUnit workUnit,
                                                                    RelationDescription rd)
        {
            // Checking if this is enabled in configuration ...
            if (!verCfg.GlobalCfg.isGenerateRevisionsForCollections())
            {
                return;
            }

            // Checking if this is not a bidirectional relation - then, a revision needs also be generated for
            // the other side of the relation.
            // relDesc can be null if this is a collection of simple values (not a relation).
            if (rd != null && rd.Bidirectional)
            {
                String    relatedEntityName = rd.ToEntityName;
                IIdMapper relatedIdMapper   = verCfg.EntCfg[relatedEntityName].GetIdMapper();

                foreach (PersistentCollectionChangeData changeData in workUnit.getCollectionChanges())
                {
                    Object relatedObj = changeData.GetChangedElement();
                    object relatedId  = relatedIdMapper.MapToIdFromEntity(relatedObj);

                    verSync.AddWorkUnit(new CollectionChangeWorkUnit(evt.Session, relatedEntityName, verCfg,
                                                                     relatedId, relatedObj));
                }
            }
        }
Пример #2
0
 public FakeRelationChange(object owningEntity, RelationDescription rd, RevisionType revisionType,
                           object index)
 {
     this.owningEntity = owningEntity;
     this.rd           = rd;
     this.revisionType = revisionType;
     this.index        = index;
 }
Пример #3
0
        //private OrmObjectsDef GetSearchScope(string name, out string localName)
        //{
        //    return GetSearchScope(name, out localName, false);
        //}

        //internal OrmObjectsDef GetSearchScope(string name, out string localName, bool throwNotFondException)
        //{
        //    Match nameMatch = GetNsNameMatch(name);
        //    OrmObjectsDef searchScope = this;
        //    if(nameMatch.Success)
        //    {

        //        if(nameMatch.Groups["prefix"].Success)
        //        {
        //            string prefix = nameMatch.Groups["prefix"].Value;
        //            ImportDescription import = Includes[prefix];
        //            if(import == null)
        //                if(throwNotFondException)
        //                    throw new KeyNotFoundException(string.Format("Import with prefix '{0}' not found.", prefix));
        //                else
        //                {
        //                    localName = null;
        //                    return null;
        //                }
        //            searchScope = import.Content;

        //        }
        //        localName = nameMatch.Groups["name"].Value;
        //    }
        //    else
        //    {
        //        localName = name;
        //    }
        //    return searchScope;
        //}

        //internal static Match GetNsNameMatch(string name)
        //{
        //    Regex regex = new Regex(@"^(?:(?'prefix'[\w]{1,}):){0,1}(?'name'[\w\d-_.]+){1}$");
        //    return regex.Match(name);
        //}

        public RelationDescription GetSimilarRelation(RelationDescription relation)
        {
            return(_relations.Find(delegate(RelationDescription match)
            {
                return
                RelationDescription.IsSimilar(relation, match);
            }));
        }
Пример #4
0
        public void AddToQuery(AuditConfiguration auditCfg, String entityName, QueryBuilder qb, Parameters parameters)
        {
            String propertyName = propertyNameGetter.Get(auditCfg);
            RelationDescription relatedEntity = CriteriaTools.GetRelatedEntity(auditCfg, entityName, propertyName);

            if (relatedEntity == null)
            {
                parameters.AddWhereWithParam(propertyName, "=", null);
            }
            else
            {
                relatedEntity.IdMapper.AddIdEqualsToQuery(parameters, null, propertyName, true);
            }
        }
        public FakeBidirectionalRelationWorkUnit(ISessionImplementor sessionImplementor, String entityName,
                                                 AuditConfiguration verCfg, Object id,
                                                 String referencingPropertyName, Object owningEntity,
                                                 RelationDescription rd, RevisionType revisionType,
                                                 Object index,
                                                 IAuditWorkUnit nestedWorkUnit)
            : base(sessionImplementor, entityName, verCfg, id)
        {
            this.nestedWorkUnit = nestedWorkUnit;

            // Adding the change for the relation.
            fakeRelationChanges = new Dictionary <String, FakeRelationChange>();
            fakeRelationChanges.Add(referencingPropertyName, new FakeRelationChange(owningEntity, rd, revisionType, index));
        }
Пример #6
0
        public void AddToQuery(AuditConfiguration auditCfg, String entityName, QueryBuilder qb, Parameters parameters)
        {
            String propertyName = propertyNameGetter.Get(auditCfg);

            RelationDescription relatedEntity = CriteriaTools.GetRelatedEntity(auditCfg, entityName, propertyName);

            if (relatedEntity == null)
            {
                throw new AuditException("This criterion can only be used on a property that is " +
                                         "a relation to another property.");
            }
            else
            {
                relatedEntity.IdMapper.AddIdEqualsToQuery(parameters, id, propertyName, equals);
            }
        }
Пример #7
0
        public static RelationDescription GetRelatedEntity(AuditConfiguration verCfg, String entityName,
                                                           String propertyName)
        {
            RelationDescription relationDesc = verCfg.EntCfg.GetRelationDescription(entityName, propertyName);

            if (relationDesc == null)
            {
                return(null);
            }

            if (relationDesc.RelationType == RelationType.TO_ONE)
            {
                return(relationDesc);
            }

            throw new AuditException("This type of relation (" + entityName + "." + propertyName +
                                     ") isn't supported and can't be used in queries.");
        }
Пример #8
0
        private void generateFakeBidirecationalRelationWorkUnits(AuditProcess auditProcess,
                                                                 IPersistentCollection newColl,
                                                                 object oldColl,
                                                                 string collectionEntityName,
                                                                 string referencingPropertyName,
                                                                 AbstractCollectionEvent evt,
                                                                 RelationDescription rd)
        {
            // First computing the relation changes
            var collectionChanges = VerCfg.EntCfg[collectionEntityName].PropertyMapper
                                    .MapCollectionChanges(evt.Session, referencingPropertyName, newColl, oldColl, evt.AffectedOwnerIdOrNull);

            // Getting the id mapper for the related entity, as the work units generated will corrspond to the related
            // entities.
            var relatedEntityName = rd.ToEntityName;
            var relatedIdMapper   = VerCfg.EntCfg[relatedEntityName].IdMapper;

            // For each collection change, generating the bidirectional work unit.
            foreach (var changeData in collectionChanges)
            {
                var relatedObj = changeData.GetChangedElement();
                var relatedId  = relatedIdMapper.MapToIdFromEntity(relatedObj);
                var revType    = (RevisionType)changeData.Data[VerCfg.AuditEntCfg.RevisionTypePropName];

                // This can be different from relatedEntityName, in case of inheritance (the real entity may be a subclass
                // of relatedEntityName).
                var realRelatedEntityName = evt.Session.BestGuessEntityName(relatedObj);

                // By default, the nested work unit is a collection change work unit.
                var nestedWorkUnit = new CollectionChangeWorkUnit(evt.Session, realRelatedEntityName, rd.MappedByPropertyName, VerCfg,
                                                                  relatedId, relatedObj);

                auditProcess.AddWorkUnit(new FakeBidirectionalRelationWorkUnit(evt.Session, realRelatedEntityName, VerCfg,
                                                                               relatedId, referencingPropertyName, evt.AffectedOwnerOrNull, rd, revType,
                                                                               changeData.GetChangedElementIndex(), nestedWorkUnit));
            }

            // We also have to generate a collection change work unit for the owning entity.
            auditProcess.AddWorkUnit(new CollectionChangeWorkUnit(evt.Session, collectionEntityName, referencingPropertyName, VerCfg,
                                                                  evt.AffectedOwnerIdOrNull, evt.AffectedOwnerOrNull));
        }
Пример #9
0
        private void OnCollectionAction(AbstractCollectionEvent evt, IPersistentCollection newColl, object oldColl,
                                        CollectionEntry collectionEntry)
        {
            String entityName = evt.GetAffectedOwnerEntityName();

            if (verCfg.EntCfg.IsVersioned(entityName))
            {
                AuditSync verSync = verCfg.AuditSyncManager.get(evt.Session);

                String ownerEntityName         = ((AbstractCollectionPersister)collectionEntry.LoadedPersister).OwnerEntityName;
                String referencingPropertyName = collectionEntry.Role.Substring(ownerEntityName.Length + 1);

                // Checking if this is not a "fake" many-to-one bidirectional relation. The relation description may be
                // null in case of collections of non-entities.
                RelationDescription rd = verCfg.EntCfg[entityName].GetRelationDescription(referencingPropertyName);
                if (rd != null && rd.MappedByPropertyName != null)
                {
                    GenerateFakeBidirecationalRelationWorkUnits(verSync, newColl, oldColl, entityName,
                                                                referencingPropertyName, evt, rd);
                }
                else
                {
                    PersistentCollectionChangeWorkUnit workUnit = new PersistentCollectionChangeWorkUnit(evt.Session,
                                                                                                         entityName, verCfg, newColl, collectionEntry, oldColl, evt.AffectedOwnerIdOrNull,
                                                                                                         referencingPropertyName);
                    verSync.AddWorkUnit(workUnit);

                    if (workUnit.ContainsWork())
                    {
                        // There are some changes: a revision needs also be generated for the collection owner
                        verSync.AddWorkUnit(new CollectionChangeWorkUnit(evt.Session, evt.GetAffectedOwnerEntityName(),
                                                                         verCfg, evt.AffectedOwnerIdOrNull, evt.AffectedOwnerOrNull));

                        GenerateBidirectionalCollectionChangeWorkUnits(verSync, evt, workUnit, rd);
                    }
                }
            }
        }
Пример #10
0
        public void AddToQuery(AuditConfiguration auditCfg, String entityName, QueryBuilder qb, Parameters parameters)
        {
            String propertyName = propertyNameGetter.Get(auditCfg);

            RelationDescription relatedEntity = CriteriaTools.GetRelatedEntity(auditCfg, entityName, propertyName);

            if (relatedEntity == null)
            {
                parameters.AddWhereWithParam(propertyName, op, value);
            }
            else
            {
                if (!"=".Equals(op) && !"<>".Equals(op))
                {
                    throw new AuditException("This type of operation: " + op + " (" + entityName + "." + propertyName +
                                             ") isn't supported and can't be used in queries.");
                }

                Object id = relatedEntity.IdMapper.MapToIdFromEntity(value);

                relatedEntity.IdMapper.AddIdEqualsToQuery(parameters, id, propertyName, "=".Equals(op));
            }
        }
Пример #11
0
        private void generateBidirectionalCollectionChangeWorkUnits(AuditProcess auditProcess,
                                                                    AbstractCollectionEvent evt,
                                                                    PersistentCollectionChangeWorkUnit workUnit,
                                                                    RelationDescription rd)
        {
            // Checking if this is enabled in configuration ...
            if (!VerCfg.GlobalCfg.GenerateRevisionsForCollections)
            {
                return;
            }

            // Checking if this is not a bidirectional relation - then, a revision needs also be generated for
            // the other side of the relation.
            // relDesc can be null if this is a collection of simple values (not a relation).
            if (rd != null && rd.Bidirectional)
            {
                var relatedEntityName = rd.ToEntityName;
                var relatedIdMapper   = VerCfg.EntCfg[relatedEntityName].IdMapper;

                foreach (var changeData in workUnit.CollectionChanges)
                {
                    var relatedObj = changeData.GetChangedElement();
                    var relatedId  = relatedIdMapper.MapToIdFromEntity(relatedObj);

                    var toPropertyNames = VerCfg.EntCfg.ToPropertyNames(evt.GetAffectedOwnerEntityName(), rd.FromPropertyName, relatedEntityName);
                    var toPropertyName  = toPropertyNames.First();

                    auditProcess.AddWorkUnit(new CollectionChangeWorkUnit(evt.Session,
                                                                          evt.Session.BestGuessEntityName(relatedObj),
                                                                          toPropertyName,
                                                                          VerCfg,
                                                                          relatedId,
                                                                          relatedObj));
                }
            }
        }
Пример #12
0
        private void addCollectionChangeWorkUnit(AuditProcess auditProcess, ISessionImplementor session, string fromEntityName, RelationDescription relDesc, object value)
        {
            // relDesc.getToEntityName() doesn't always return the entity name of the value - in case
            // of subclasses, this will be root class, no the actual class. So it can't be used here.
            string toEntityName;
            object id;

            if (value is INHibernateProxy newValueAsProxy)
            {
                toEntityName = session.BestGuessEntityName(value);
                id           = newValueAsProxy.HibernateLazyInitializer.Identifier;
                // We've got to initialize the object from the proxy to later read its state.
                value = Toolz.GetTargetFromProxy(session, newValueAsProxy);
            }
            else
            {
                toEntityName = session.GuessEntityName(value);

                var idMapper = VerCfg.EntCfg[toEntityName].IdMapper;
                id = idMapper.MapToIdFromEntity(value);
            }

            var toPropertyNames = VerCfg.EntCfg.ToPropertyNames(fromEntityName, relDesc.FromPropertyName, toEntityName);
            var toPropertyName  = toPropertyNames.First();

            auditProcess.AddWorkUnit(new CollectionChangeWorkUnit(session, toEntityName, toPropertyName, VerCfg, id, value));
        }
Пример #13
0
        private void GenerateBidirectionalCollectionChangeWorkUnits(AuditSync verSync, IEntityPersister entityPersister,
                                                                    String entityName, Object[] newState, Object[] oldState,
                                                                    ISessionImplementor session)
        {
            // Checking if this is enabled in configuration ...
            if (!verCfg.GlobalCfg.isGenerateRevisionsForCollections())
            {
                return;
            }

            // Checks every property of the entity, if it is an "owned" to-one relation to another entity.
            // If the value of that property changed, and the relation is bi-directional, a new revision
            // for the related entity is generated.
            String[] propertyNames = entityPersister.PropertyNames;

            for (int i = 0; i < propertyNames.GetLength(0); i++)
            {
                String propertyName         = propertyNames[i];
                RelationDescription relDesc = verCfg.EntCfg.GetRelationDescription(entityName, propertyName);
                if (relDesc != null && relDesc.Bidirectional && relDesc.RelationType == RelationType.TO_ONE &&
                    relDesc.Insertable)
                {
                    // Checking for changes
                    Object oldValue = oldState == null ? null : oldState[i];
                    Object newValue = newState == null ? null : newState[i];

                    if (!Toolz.EntitiesEqual(session, oldValue, newValue))
                    {
                        // We have to generate changes both in the old collection (size decreses) and new collection
                        // (size increases).

                        //<TODO Simon: doua if-uri cu cod duplicat, refact.
                        if (newValue != null)
                        {
                            // relDesc.getToEntityName() doesn't always return the entity name of the value - in case
                            // of subclasses, this will be root class, no the actual class. So it can't be used here.
                            String toEntityName;

                            // Java: Serializable id
                            object id;

                            if (newValue is INHibernateProxy)
                            {
                                INHibernateProxy hibernateProxy = (INHibernateProxy)newValue;
                                toEntityName = session.BestGuessEntityName(newValue);
                                id           = hibernateProxy.HibernateLazyInitializer.Identifier;
                                // We've got to initialize the object from the proxy to later read its state.
                                newValue = NHibernate.Envers.Tools.Toolz.GetTargetFromProxy(session.Factory, hibernateProxy);
                            }
                            else
                            {
                                toEntityName = session.GuessEntityName(newValue);

                                IIdMapper idMapper = verCfg.EntCfg[toEntityName].GetIdMapper();
                                id = idMapper.MapToIdFromEntity(newValue);
                            }

                            verSync.AddWorkUnit(new CollectionChangeWorkUnit(session, toEntityName, verCfg, id, newValue));
                        }

                        if (oldValue != null)
                        {
                            String toEntityName;
                            object id;

                            if (oldValue is INHibernateProxy)
                            {
                                INHibernateProxy hibernateProxy = (INHibernateProxy)oldValue;
                                toEntityName = session.BestGuessEntityName(oldValue);
                                id           = hibernateProxy.HibernateLazyInitializer.Identifier;
                                // We've got to initialize the object as we'll read it's state anyway.
                                oldValue = Toolz.GetTargetFromProxy(session.Factory, hibernateProxy);
                            }
                            else
                            {
                                toEntityName = session.GuessEntityName(oldValue);

                                IIdMapper idMapper = verCfg.EntCfg[toEntityName].GetIdMapper();
                                id = idMapper.MapToIdFromEntity(oldValue);
                            }

                            verSync.AddWorkUnit(new CollectionChangeWorkUnit(session, toEntityName, verCfg, id, oldValue));
                        }
                    }
                }
            }
        }
Пример #14
0
        protected void ProcessM2M(Dictionary <Column, Column> columns, OrmObjectsDef odef)
        {
            List <Pair <string> > tables = new List <Pair <string> >();

            using (DbConnection conn = GetDBConn(_server, _m, _db, _i, _user, _psw))
            {
                using (DbCommand cmd = conn.CreateCommand())
                {
                    cmd.CommandType = CommandType.Text;
                    cmd.CommandText = @"select table_schema,table_name from INFORMATION_SCHEMA.TABLE_CONSTRAINTS
						where constraint_type = 'FOREIGN KEY'
						group by table_schema,table_name
						having count(*) = 2"                        ;
                    conn.Open();

                    using (DbDataReader reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            tables.Add(new Pair <string>(reader.GetString(reader.GetOrdinal("table_schema")),
                                                         reader.GetString(reader.GetOrdinal("table_name"))));
                        }
                    }
                }
            }

            foreach (Pair <string> p in tables)
            {
                string            underlying = GetEntityName(p.First, p.Second);
                EntityDescription ued        = odef.GetEntity(underlying);
                using (DbConnection conn = GetDBConn(_server, _m, _db, _i, _user, _psw))
                {
                    using (DbCommand cmd = conn.CreateCommand())
                    {
                        cmd.CommandType = CommandType.Text;
                        cmd.CommandText = @"select cc.table_schema,cc.table_name,cc2.column_name,rc.delete_rule
						from INFORMATION_SCHEMA.constraint_column_usage cc
						join INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc on rc.unique_constraint_name = cc.constraint_name
						join INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc on tc.constraint_name = rc.constraint_name
						join INFORMATION_SCHEMA.constraint_column_usage cc2 on cc2.constraint_name = tc.constraint_name and cc2.table_schema = tc.table_schema and cc2.table_name = tc.table_name
						where tc.table_name = @tbl and tc.table_schema = @schema
						and tc.constraint_type = 'FOREIGN KEY'"                        ;

                        DbParameter tbl = cmd.CreateParameter();
                        tbl.ParameterName = "tbl";
                        tbl.Value         = p.Second;
                        cmd.Parameters.Add(tbl);

                        DbParameter schema = cmd.CreateParameter();
                        schema.ParameterName = "schema";
                        schema.Value         = p.First;
                        cmd.Parameters.Add(schema);

                        conn.Open();

                        List <LinkTarget> targets = new List <LinkTarget>();
                        using (DbDataReader reader = cmd.ExecuteReader())
                        {
                            while (reader.Read())
                            {
                                //string ename = reader.GetString(reader.GetOrdinal("table_schema")) + "." +
                                //    reader.GetString(reader.GetOrdinal("table_name"));
                                bool deleteCascade = false;
                                switch (reader.GetString(reader.GetOrdinal("delete_rule")))
                                {
                                case "NO ACTION":
                                    break;

                                case "CASCADE":
                                    deleteCascade = true;
                                    break;

                                default:
                                    throw new NotSupportedException("Cascade " + reader.GetString(reader.GetOrdinal("delete_rule")) + " is not supported");
                                }
                                bool       c;
                                LinkTarget lt = new LinkTarget(
                                    GetEntity(odef,
                                              reader.GetString(reader.GetOrdinal("table_schema")),
                                              reader.GetString(reader.GetOrdinal("table_name")), out c),
                                    reader.GetString(reader.GetOrdinal("column_name")), deleteCascade);
                                targets.Add(lt);
                            }
                        }
                        RelationDescription rd = odef.GetSimilarRelation(
                            new RelationDescription(targets[0], targets[1], null, null));
                        if (rd == null)
                        {
                            rd = new RelationDescription(targets[0], targets[1],
                                                         GetTable(odef, p.First, p.Second), ued);
                            odef.Relations.Add(rd);
                        }
                    }
                }
            }
        }
Пример #15
0
        internal protected void FillRelations()
        {
            XmlNodeList relationNodes;

            #region Relations
            relationNodes = _ormXmlDocument.DocumentElement.SelectNodes(string.Format("{0}:EntityRelations/{0}:Relation", OrmObjectsDef.NS_PREFIX), _nsMgr);

            foreach (XmlNode relationNode in relationNodes)
            {
                XmlNode leftTargetNode  = relationNode.SelectSingleNode(string.Format("{0}:Left", OrmObjectsDef.NS_PREFIX), _nsMgr);
                XmlNode rightTargetNode = relationNode.SelectSingleNode(string.Format("{0}:Right", OrmObjectsDef.NS_PREFIX), _nsMgr);

                XmlElement relationElement    = (XmlElement)relationNode;
                string     relationTableId    = relationElement.GetAttribute("table");
                string     underlyingEntityId = relationElement.GetAttribute("underlyingEntity");
                string     disabledValue      = relationElement.GetAttribute("disabled");

                XmlElement leftTargetElement       = (XmlElement)leftTargetNode;
                string     leftLinkTargetEntityId  = leftTargetElement.GetAttribute("entity");
                XmlElement rightTargetElement      = (XmlElement)rightTargetNode;
                string     rightLinkTargetEntityId = rightTargetElement.GetAttribute("entity");

                string leftFieldName  = leftTargetElement.GetAttribute("fieldName");
                string rightFieldName = rightTargetElement.GetAttribute("fieldName");

                bool leftCascadeDelete  = XmlConvert.ToBoolean(leftTargetElement.GetAttribute("cascadeDelete"));
                bool rightCascadeDelete = XmlConvert.ToBoolean(rightTargetElement.GetAttribute("cascadeDelete"));

                TableDescription relationTable = _ormObjectsDef.GetTable(relationTableId);

                EntityDescription underlyingEntity;
                if (string.IsNullOrEmpty(underlyingEntityId))
                {
                    underlyingEntity = null;
                }
                else
                {
                    underlyingEntity = _ormObjectsDef.GetEntity(underlyingEntityId);
                }

                bool disabled;
                if (string.IsNullOrEmpty(disabledValue))
                {
                    disabled = false;
                }
                else
                {
                    disabled = XmlConvert.ToBoolean(disabledValue);
                }



                EntityDescription leftLinkTargetEntity = _ormObjectsDef.GetEntity(leftLinkTargetEntityId);

                EntityDescription rightLinkTargetEntity = _ormObjectsDef.GetEntity(rightLinkTargetEntityId);

                LinkTarget leftLinkTarget  = new LinkTarget(leftLinkTargetEntity, leftFieldName, leftCascadeDelete);
                LinkTarget rightLinkTarget = new LinkTarget(rightLinkTargetEntity, rightFieldName, rightCascadeDelete);

                RelationDescription relation = new RelationDescription(leftLinkTarget, rightLinkTarget, relationTable, underlyingEntity, disabled);
                _ormObjectsDef.Relations.Add(relation);
            }
            #endregion
            #region Relations
            relationNodes = _ormXmlDocument.DocumentElement.SelectNodes(string.Format("{0}:EntityRelations/{0}:SelfRelation", OrmObjectsDef.NS_PREFIX), _nsMgr);

            foreach (XmlNode relationNode in relationNodes)
            {
                XmlNode directTargetNode  = relationNode.SelectSingleNode(string.Format("{0}:Direct", OrmObjectsDef.NS_PREFIX), _nsMgr);
                XmlNode reverseTargetNode = relationNode.SelectSingleNode(string.Format("{0}:Reverse", OrmObjectsDef.NS_PREFIX), _nsMgr);

                XmlElement relationElement    = (XmlElement)relationNode;
                string     relationTableId    = relationElement.GetAttribute("table");
                string     underlyingEntityId = relationElement.GetAttribute("underlyingEntity");
                string     disabledValue      = relationElement.GetAttribute("disabled");
                string     entityId           = relationElement.GetAttribute("entity");

                XmlElement directTargetElement  = (XmlElement)directTargetNode;
                XmlElement reverseTargetElement = (XmlElement)reverseTargetNode;

                string directFieldName  = directTargetElement.GetAttribute("fieldName");
                string reverseFieldName = reverseTargetElement.GetAttribute("fieldName");

                bool directCascadeDelete  = XmlConvert.ToBoolean(directTargetElement.GetAttribute("cascadeDelete"));
                bool reverseCascadeDelete = XmlConvert.ToBoolean(reverseTargetElement.GetAttribute("cascadeDelete"));

                TableDescription relationTable = _ormObjectsDef.GetTable(relationTableId);

                EntityDescription underlyingEntity;
                if (string.IsNullOrEmpty(underlyingEntityId))
                {
                    underlyingEntity = null;
                }
                else
                {
                    underlyingEntity = _ormObjectsDef.GetEntity(underlyingEntityId);
                }

                bool disabled;
                if (string.IsNullOrEmpty(disabledValue))
                {
                    disabled = false;
                }
                else
                {
                    disabled = XmlConvert.ToBoolean(disabledValue);
                }



                EntityDescription entity = _ormObjectsDef.GetEntity(entityId);

                SelfRelationTarget directTarget  = new SelfRelationTarget(directFieldName, directCascadeDelete);
                SelfRelationTarget reverseTarget = new SelfRelationTarget(reverseFieldName, reverseCascadeDelete);

                SelfRelationDescription relation = new SelfRelationDescription(entity, directTarget, reverseTarget, relationTable, underlyingEntity, disabled);
                _ormObjectsDef.SelfRelations.Add(relation);
            }
            #endregion
        }