/// <summary>
        /// Build the folder tree below this node.
        /// </summary>
        /// <param name="entityRow"></param>
        private void BuildTree(EntityRow entityRow)
        {
            EntityTreeRow[] entityTreeRows = entityRow.GetEntityTreeRowsByFK_Entity_EntityTree_ParentId();

            foreach (EntityTreeRow entityTreeRow in entityTreeRows)
            {
                Boolean inserted = false;

                // Add the new tree relations in alphabetical order.
                for (int targetIndex = 0; !inserted && targetIndex < this.Children.Count; targetIndex++)
                {
                    if (entityTreeRow.EntityRowByFK_Entity_EntityTree_ChildId.Name.CompareTo(this.Children[targetIndex].Entity.Name) < 0)
                    {
                        this.Children.Insert(targetIndex, new FolderTreeNode(entityTreeRow));
                        inserted = true;
                    }
                }

                // Any we missed go at the end.
                if (!inserted)
                {
                    this.Children.Add(new FolderTreeNode(entityTreeRow));
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// Update the entity with the information current in the DataModel.
        /// </summary>
        /// <param name="entityRow">The entity row to base the update on.</param>
        public override void Update(EntityRow entityRow)
        {
            // HACK: CommissionSchedule should be in all blotters.
            DebtClassRow debtClassRow = DataModel.DebtClass.DebtClassKey.Find(entityRow.EntityId);

            base.Update(entityRow);

            if (!this.Modified && this.EntityId == entityRow.EntityId)
            {
                if (debtClassRow != null && debtClassRow.CommissionScheduleRow != null)
                {
                    if (this.commissionSchedule != null)
                    {
                        this.commissionSchedule.Update(debtClassRow.CommissionScheduleRow);
                    }
                    else
                    {
                        this.commissionSchedule = new CommissionSchedule(debtClassRow.CommissionScheduleRow);
                        this.commissionSchedule.PropertyChanged += this.OnCommissionScheduleChanged;
                    }
                }
                else if (this.commissionSchedule != null)
                {
                    this.commissionSchedule.PropertyChanged -= this.OnCommissionScheduleChanged;
                    this.commissionSchedule = null;
                }

                this.Modified = false;
            }
        }
        /// <summary>
        /// Filter what rows are included in the list based on the value of includeRemovedUsers.
        /// </summary>
        /// <param name="row">The row to examine.</param>
        /// <returns>False if IncludeRemovedUsers is false and the user has been removed; true otherwise.</returns>
        protected override bool Filter(DataRow row)
        {
            RightsHolderRow rightsHolderRow = null;

            UserRow[] userRow;

            if (row is RightsHolderRow)
            {
                rightsHolderRow = row as RightsHolderRow;
            }
            else if (row is EntityRow)
            {
                EntityRow entityRow = row as EntityRow;

                if (entityRow.GetRightsHolderRows().Length != 0)
                {
                    rightsHolderRow = entityRow.GetRightsHolderRows()[0];
                }
            }
            else
            {
                throw new RowNotHandledException("row isn't the right kind of row");
            }

            userRow = rightsHolderRow.GetUserRows();

            return(this.includeRemovedUsers || userRow.Length == 0 || !userRow[0].IsRemoved);
        }
Beispiel #4
0
        /// <summary>
        /// Initialize the list of root folders.
        /// </summary>
        private void InitializeFolders(Guid parentId)
        {
            lock (DataModel.SyncRoot)
            {
                LambdaComparer <FolderTreeNode> folderTreeNodeComparer = new LambdaComparer <FolderTreeNode>((l, r) => l.Entity.Name.CompareTo(r.Entity.Name));
                List <FolderTreeNode>           folders = new List <FolderTreeNode>();
                EntityRow parentEntityRow = DataModel.Entity.EntityKey.Find(parentId);

                foreach (EntityTreeRow entityTreeRow in parentEntityRow.GetEntityTreeRowsByFK_Entity_EntityTree_ParentId())
                {
                    FolderTreeNode folderTreeNode = new FolderTreeNode()
                    {
                        Entity = new Entity(entityTreeRow.EntityRowByFK_Entity_EntityTree_ChildId)
                    };
                    Int32 index = folders.BinarySearch(folderTreeNode, folderTreeNodeComparer);

                    if (index < 0)
                    {
                        index = ~index;
                    }

                    folders.Insert(index, folderTreeNode);
                }

                this.Dispatcher.BeginInvoke(new Action(() => this.Folders = folders), DispatcherPriority.Normal);
            }
        }
        /// <summary>
        /// See if the ChildId is a ParentId to stop circular reference.
        /// </summary>
        /// <param name="transaction"></param>
        /// <param name="parent"></param>
        /// <param name="childId"></param>
        /// <returns></returns>
        private bool IsParentEntity(DataModelTransaction transaction, EntityRow parent, Guid childId)
        {
            parent.AcquireReaderLock(transaction.TransactionId, DataModel.LockTimeout);
            try
            {
                //Make sure we are not adding a child element that is also a parent.
                foreach (EntityTreeRow entityRow in parent.GetEntityTreeRowsByFK_Entity_EntityTree_ChildId())
                {
                    entityRow.AcquireWriterLock(transaction.TransactionId, DataModel.LockTimeout);
                    try
                    {
                        if (entityRow.ParentId == childId)
                        {
                            return(true);
                        }

                        if (IsParentEntity(transaction, entityRow.EntityRowByFK_Entity_EntityTree_ParentId, childId) == true)
                        {
                            return(true);
                        }
                    }
                    finally
                    {
                        entityRow.ReleaseLock(transaction.TransactionId);
                    }
                }
            }
            finally
            {
                parent.ReleaseReaderLock(transaction.TransactionId);
            }
            return(false);
        }
Beispiel #6
0
        /// <summary>
        /// Create an entity.
        /// </summary>
        /// <param name="entityRow">The description of the entity.</param>
        public Entity(EntityRow entityRow)
        {
            // Initialize the object
            this.createdTime      = entityRow.CreatedTime.ToLocalTime();
            this.description      = entityRow.IsDescriptionNull() ? String.Empty : entityRow.Description;
            this.entityId         = entityRow.EntityId;
            this.modifiedTime     = entityRow.ModifiedTime.ToLocalTime();
            this.name             = entityRow.Name;
            this.imageId          = entityRow.ImageId;
            this.imageData        = entityRow.ImageRow.Image;
            this.isReadOnly       = entityRow.IsReadOnly;
            this.isHidden         = entityRow.IsHidden;
            this.rowVersion       = entityRow.RowVersion;
            this.tenantId         = entityRow.TenantId;
            this.typeId           = entityRow.TypeId;
            this.typeName         = entityRow.TypeRow.Description;
            this.viewerType       = entityRow.TypeRow.ViewerType;
            this.propertiesWindow = null;

            if (this.NeedLateLoad())
            {
                this.finishedLoading = false;
                DataModel.EndMerge  += this.FinishLateLoad;
            }
            else
            {
                this.finishedLoading = true;
                this.FinishLoad();
            }
        }
Beispiel #7
0
        /// <summary>
        /// Find the nearest ancestor DebtClass that has a value for a particular field.
        /// </summary>
        /// <param name="debtClassId">The DebtClassId of the debt class to start with.</param>
        /// <param name="field">The column to look at.</param>
        /// <returns>The row of the first debt class found to have a value for the indicated field (including the initial debt class).</returns>
        private static DebtClassRow FindParentWithField(Guid debtClassId, DataColumn field)
        {
            DebtClassRow parent    = null;
            DebtClassRow debtClass = DataModel.DebtClass.DebtClassKey.Find(debtClassId);
            Guid         typeId    = DataModel.Entity.EntityKey.Find(debtClassId).TypeId;

            while (debtClass != null && parent == null)
            {
                EntityRow       child   = DataModel.Entity.EntityKey.Find(debtClass.DebtClassId);
                EntityTreeRow[] parents = child.GetEntityTreeRowsByFK_Entity_EntityTree_ChildId();

                if (!debtClass.IsNull(field))
                {
                    parent = debtClass;
                }

                if (parents.Length != 0)
                {
                    debtClass = DataModel.DebtClass.DebtClassKey.Find(parents[0].ParentId);
                }
                else
                {
                    debtClass = null;
                }
            }

            return(parent);
        }
        /// <summary>
        /// Get a list of the entity names that describes the path to this object.
        /// </summary>
        /// <param name="current">The current entity in the path to the object.</param>
        /// <param name="target">The entityId of the entity we're building a path for.</param>
        /// <returns>The list of entity names that represent the path to the object.</returns>
        private List <string> GetLocation(EntityRow current, Guid target)
        {
            List <string> list = null;

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

            // If current is the entity that we're looking for, we'll create our own list.
            if (current.EntityId == target)
            {
                list = new List <string>();
            }
            // .... Otherwise, see if we can generate a path list from one of our children.
            else
            {
                foreach (EntityTreeRow node in current.GetEntityTreeRowsByFK_Entity_EntityTree_ParentId())
                {
                    list = this.GetLocation(node.EntityRowByFK_Entity_EntityTree_ChildId, target);
                    if (list != null)
                    {
                        break;
                    }
                }
            }

            if (list != null)
            {
                list.Insert(0, current.Name);
            }

            return(list);
        }
Beispiel #9
0
        /// <summary>
        /// Count all of the working orders and matches in blotters below the parent entity. The caller should lock the DataModel.
        /// </summary>
        /// <param name="parent">The parent entity.</param>
        /// <param name="totalRecords">The total number of records. Should initially be 0.</param>
        /// <param name="matchedRecords">The number of of valid matches. Should initially be 0.</param>
        private void CountAccounts(EntityRow parent, ref int totalRecords, ref int matchedRecords)
        {
            BlotterRow blotter = DataModel.Blotter.BlotterKey.Find(parent.EntityId);

            if (blotter != null)
            {
                MatchRow[]        matches = blotter.GetMatchRows();
                WorkingOrderRow[] orders  = blotter.GetWorkingOrderRows();

                matchedRecords += matches.Length;

                // Count up the credit cards for everything.
                foreach (WorkingOrderRow order in orders)
                {
                    Guid             security = order.SecurityId;
                    ConsumerTrustRow trust    = DataModel.ConsumerTrust.ConsumerTrustKey.Find(security);

                    if (trust != null)
                    {
                        totalRecords += trust.ConsumerRow.GetCreditCardRows().Length;
                    }
                }
            }

            // Do the same for all of the blotters under this one.
            foreach (EntityTreeRow tree in DataModel.EntityTree)
            {
                if (tree.ParentId == parent.EntityId)
                {
                    this.CountAccounts(tree.EntityRowByFK_Entity_EntityTree_ChildId, ref totalRecords, ref matchedRecords);
                }
            }
        }
Beispiel #10
0
        /// <summary>
        /// Determine whether a rights holder has particular rights on an entity.
        /// </summary>
        /// <param name="rightsHolderRow">The rights holder</param>
        /// <param name="entity">The entity.</param>
        /// <param name="right">The required rights.</param>
        /// <returns>True if the rights holder has the specified rights to the entity.</returns>
        public static Boolean HasAccess(RightsHolderRow rightsHolderRow, EntityRow entity, AccessRight right)
        {
            // TODO: Remove this method? Current there are no calls to this method.
            // NOTE: This must be used in a background thread.

            Boolean grantedAccess = false;

            lock (DataModel.SyncRoot)
            {
                UserRow userRow = DataModel.User.UserKey.Find(rightsHolderRow.RightsHolderId);

                // See if the rights holder themself has access.
                foreach (AccessControlRow accessControlRow in rightsHolderRow.GetAccessControlRows())
                {
                    if (accessControlRow.EntityId == entity.EntityId &&
                        (accessControlRow.AccessRightRow.AccessRightCode & right) == right)
                    {
                        grantedAccess = true;
                    }
                }

                if (userRow != null)
                {
                    foreach (GroupUsersRow groupUsersRow in userRow.GetGroupUsersRows())
                    {
                        if (AccessControl.HasAccess(groupUsersRow.GroupRow.RightsHolderRow, entity, right))
                        {
                            grantedAccess = true;
                        }
                    }
                }
            }

            return(grantedAccess);
        }
        /// <summary>
        /// Filter what rows are included in the list based on Filter.
        /// </summary>
        /// <param name="row">The row to examine.</param>
        /// <returns>The return value of Filter.</returns>
        protected override bool Filter(DataRow row)
        {
            BlotterRow blotterRow = null;

            if (row is BlotterRow)
            {
                blotterRow = row as BlotterRow;
            }
            else if (row is EntityRow)
            {
                EntityRow entityRow = row as EntityRow;
                if (entityRow.GetBlotterRows().Length != 0)
                {
                    blotterRow = entityRow.GetBlotterRows()[0];
                }
            }
            else
            {
                throw new RowNotHandledException("row isn't the right kind of row");
            }

            if (blotterRow == null)
            {
                return(false);
            }

            return(true);
        }
Beispiel #12
0
        static public string AutoGenKey(ref EntityRow row)
        {
            var table = row.Table;

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

            var pk     = row.Table.PrimaryKey;
            var keyGen = pk.GetAttributesEntity().KeyGen;

            if (string.IsNullOrEmpty(keyGen))
            {
                return(null);
            }
            var  tableAttri = table.GetAttributesEntity();
            long keyIndex   = tableAttri.KeyCount + 1;
            var  key        = CommonExtension.StringFormat(keyGen, keyIndex);

            tableAttri.KeyCount = keyIndex;
            row.SetValue(table.PrimaryKey, key, true);
            return(key);
            //Table.GetAttributesEntity().KeyCount
        }
Beispiel #13
0
        /// <summary>
        /// Update the User based on an EntityRow.
        /// </summary>
        /// <param name="entityRow">The row to update from.</param>
        public override void Update(EntityRow entityRow)
        {
            UserRow userRow = DataModel.User.UserKey.Find(entityRow.EntityId);

            base.Update(entityRow);

            if (userRow != null)
            {
                if (!this.Modified && entityRow.EntityId == this.EntityId)
                {
                    this.IdentityName = userRow.IdentityName;
                    this.isRemoved    = userRow.IsRemoved;

                    this.groups.Clear();
                    foreach (GroupUsersRow groupRow in userRow.GetGroupUsersRows())
                    {
                        this.groups.Add(Entity.New(groupRow.GroupRow.RightsHolderRow.EntityRow) as Group);
                    }

                    // The information kept in AD takes a really long time to come back, so we'll grab it in another thread.
                    ThreadPoolHelper.QueueUserWorkItem(data => this.LoadAdInformation(data as String), this.IdentityName);
                }

                this.rowVersion = userRow.RowVersion;
            }
        }
 private void UnregistRowTransaction(EntityRow row)
 {
     if (State != DataTransactionState.Begin)
     {
         return;
     }
     rowTransactionMap.Remove(row.InternalId);
 }
        /// <summary>
        /// Creates a node from an object in the data model.
        /// </summary>
        /// <param name="entityRow">The data model object used to create a node in the TreeView.</param>
        public FolderTreeNode(EntityRow entityRow)
        {
            // Initialize the object from the data in the record.
            this.relationId = Guid.Empty;
            this.rowVersion = Int64.MinValue;
            this.entity     = Entity.New(entityRow);

            this.BuildTree(entityRow);
        }
Beispiel #16
0
 /// <summary>
 /// Determines if a given blotter is visible on the current document.
 /// </summary>
 /// <param name="parentRow">The current object in the recursive search.</param>
 /// <param name="blotterId">The blotter id to be found.</param>
 /// <returns>true if the blotter is part of the tree structure.</returns>
 public static List <Guid> GetDescendants(EntityRow parentRow)
 {
     lock (DataModel.SyncRoot)
     {
         List <Guid> descendants = new List <Guid>();
         GetDescendants(descendants, parentRow);
         return(descendants);
     }
 }
Beispiel #17
0
 private void Restore(EntityRow srcRow)
 {
     if (srcRow.ItemArray.Length != Row.ItemArray.Length)
     {
         throw new Exception("EntityRowTransaction.Restore()");
     }
     Row.Attributes = srcRow.Attributes;
     srcRow.CopyItermArrayTo(Row);
 }
Beispiel #18
0
        /// <summary>
        /// Create a new User object based on an entity row.
        /// </summary>
        /// <param name="entityRow">An entity row from the DataModel.</param>
        public User(EntityRow entityRow) : base(entityRow)
        {
            if (this.groups == null)
            {
                this.groups = new ObservableCollection <Group>();
            }

            this.groups.CollectionChanged += this.OnGroupsChanged;
        }
        static public EntityRelation BuildEntityRelation(EntityRow parentRow, EntityColumn column)
        {
            var value = parentRow[column];

            if (value == null)
            {
                return(null);
            }
            return(BuildEntityRelation(parentRow, value.ToString()));
        }
Beispiel #20
0
        /// <summary>
        /// Create a new Entity object from an EntityRow from the DataModel.
        /// </summary>
        /// <param name="entityRow">The entity row to base the entity object from.</param>
        /// <returns>The newly populated Entity object.</returns>
        public static Entity New(EntityRow entityRow)
        {
            String[] typeParts = entityRow.TypeRow.Type.Split(',');
            Assembly assembly;
            string   className;

            Entity.LoadAssembly(entityRow.TypeRow.Type, out assembly, out className);
            return(assembly.CreateInstance(className, false, BindingFlags.CreateInstance, null,
                                           new object[] { entityRow }, CultureInfo.InvariantCulture, null) as Entity);
        }
        private void DoRegist(EntityTable registedTable, EntityRow row, OPStatus opStatus)
        {
            IEntityRowAttributes rowAttributes = row.GetAttributesEntity();
            bool isExist       = registedTable.Rows.Contains(row);
            bool inTransaction = (State == DataTransactionState.Begin);

            switch (opStatus)
            {
            case OPStatus.Delete:
                if (isExist)
                {
                    registedTable.Rows.Remove(row);
                }
                if (rowAttributes.Status == OPStatus.Add)
                {
                    UnregistRowTransaction(row);
                }
                else
                {
                    RegistRowTransaction(row, true);
                    rowAttributes.Status = opStatus;
                }
                break;

            case OPStatus.Add:
                if (!isExist)
                {
                    registedTable.Rows.Add(row);
                }
                RegistRowTransaction(row, false);
                rowAttributes.Status = opStatus;
                break;

            case OPStatus.Update:
                if (rowAttributes.Status != OPStatus.Add && rowAttributes.Status != OPStatus.Delete)
                {
                    rowAttributes.Status = opStatus;
                    //100/11/30 by Feng,避免已經BeginUpdate後才又有Table被查詢regist
                    RegistRowTransaction(row, true);
                }
                break;

            case OPStatus.Steady:
                if (!isExist)
                {
                    rowAttributes.Status = opStatus;
                    registedTable.Rows.Add(row);
                    RegistRowTransaction(row, true);
                }
                break;

            default:
                throw new Exception("EntityTableTransaction.Regist()");
            }
        }
        /// <summary>
        /// Creates a node from an object in the data model.
        /// </summary>
        /// <param name="entityTreeRow">The data model object used to create a node in the TreeView.</param>
        public FolderTreeNode(EntityTreeRow entityTreeRow)
        {
            EntityRow entityRow = entityTreeRow.EntityRowByFK_Entity_EntityTree_ChildId;

            // Initialize the object from the data in the record.
            this.relationId = entityTreeRow.EntityTreeId;
            this.rowVersion = entityTreeRow.RowVersion;
            this.entity     = Entity.New(entityRow);

            this.BuildTree(entityRow);
        }
        /// <summary>
        /// Handle an update to the data model. If no changes have been made to the entity, re-populate the dialog box.
        /// </summary>
        private void OnEndMerge(object sender, EventArgs eventArgs)
        {
            if (this.MustRedisplay)
            {
                EntityRow entityRow = DataModel.Entity.EntityKey.Find(this.entityId);

                FluidTrade.Core.ThreadPoolHelper.QueueUserWorkItem(data => this.Populate(data as Entity), entityRow == null ? null : Entity.New(entityRow));

                this.MustRedisplay = false;
            }
        }
        /// <summary>
        /// Placeholder
        /// </summary>
        /// <param name="record"></param>
        public override void Update(EntityTree record)
        {
            DataModel            dataModel     = new DataModel();
            DataModelTransaction transaction   = DataModelTransaction.Current;
            EntityTreeRow        entityTreeRow = DataModel.EntityTree.EntityTreeKey.Find(record.RowId);
            EntityRow            child         = DataModel.Entity.EntityKey.Find(record.ChildId);
            EntityRow            parent        = DataModel.Entity.EntityKey.Find(record.ParentId);
            String childName;

            entityTreeRow.AcquireWriterLock(transaction);

            if (!DataModelFilters.HasAccess(transaction, TradingSupport.UserId, entityTreeRow.ParentId, AccessRight.Write))
            {
                throw new SecurityException("Current user does not have write access to the old parent entity");
            }

            if (!DataModelFilters.HasAccess(transaction, TradingSupport.UserId, record.ParentId, AccessRight.Write))
            {
                throw new SecurityException("Current user does not have write access to the new parent entity");
            }

            parent.AcquireWriterLock(transaction);
            child.AcquireReaderLock(transaction);
            childName = child.Name;
            child.ReleaseLock(transaction.TransactionId);

            if (record.ChildId == record.ParentId)
            {
                throw new FaultException <ArgumentFault>(new ArgumentFault("Create EntityTree"),
                                                         new FaultReason(String.Format("Cannot add {0} as a child of this element because it wil create a circular relationship", childName)));
            }


            if (IsParentEntity(transaction, parent, record.ChildId) == true)
            {
                throw new FaultException <ArgumentFault>(new ArgumentFault("Create EntityTree"),
                                                         new FaultReason(String.Format("Cannot add {0} as a child of this element because it wil create a circular relationship", childName)));
            }

            if (!EntityPersistence.IsNameUnique(transaction, parent, childName))
            {
                throw new FaultException <RecordExistsFault>(
                          new RecordExistsFault("Entity", new object[] { childName }),
                          "An entity with this name already exists");
            }

            dataModel.UpdateEntityTree(
                record.ChildId,
                entityTreeRow.EntityTreeId,
                new object[] { entityTreeRow.EntityTreeId },
                null,
                record.ParentId,
                entityTreeRow.RowVersion);
        }
        /// <summary>
        /// Create a new EffectiveDebtClass from an EntityRow.
        /// </summary>
        /// <param name="entityRow">The entity row to base the debt class on.</param>
        public EffectiveDebtClass(EntityRow entityRow)
            : base(entityRow)
        {
            // This would have retrieved values from the hierarchy.
            //this.Construct();

            // We watch DebtClass in case data in one of our ancestors changes.
            DataModel.DebtClass.DebtClassRowChanged += this.OnDebtClassChanged;
            // And we watch EntityTree in case we or one of our ancestors moves.
            //DataModel.EntityTree.EntityTreeRowChanged += this.OnEntityTreeChanged;
        }
Beispiel #26
0
        static public EntityRowProxy <TEntity> GenProxy <TEntity>(this EntityRow dataRow, IEntityTableSource entityTableSource = null)
        {
            string proxyKey = typeof(EntityRowProxy <TEntity>).FullName;
            EntityRowProxy <TEntity> proxy;

            if (!dataRow.TryGetCache(proxyKey, out proxy))
            {
                proxy = new EntityRowProxy <TEntity>(dataRow, entityTableSource);
                dataRow.AddCache(proxyKey, proxy);
            }
            return(proxy);
        }
Beispiel #27
0
 /// <summary>
 /// Clears the given blotter of all working orders.
 /// </summary>
 /// <param name="dataModelClient">Used to execute Web Services.</param>
 /// <param name="blotterName">The name of the blotter to be reset.</param>
 private static void DestroyWorkingOrders(DataModelClient dataModelClient, String blotterName)
 {
     // Destroy all the Working Orders for Kai Hitori
     foreach (WorkingOrderRow workingOrderRow in DataModel.WorkingOrder)
     {
         EntityRow entityRow = workingOrderRow.BlotterRow.EntityRow;
         if (entityRow.Name == blotterName)
         {
             dataModelClient.DestroyWorkingOrder(workingOrderRow.RowVersion, new object[] { workingOrderRow.WorkingOrderId });
         }
     }
 }
Beispiel #28
0
        private void sel_topic_list_sel_changed(object sender, RoutedEventArgs e)
        {
            EntityRow sel  = this.entity_list.SelectedValue as EntityRow;
            Guid?     guid = this.sel_topic_list.SelectedValue as Guid?;

            if ((sel is null) || (sel.type is null) || (sel.guid is null) || (sel.topics is null) || (guid is null))
            {
                this.topic_view_but.IsEnabled = false;
                return;
            }
            this.topic_view_but.IsEnabled = (guid.Value != this.guid);
        }
        /// <summary>
        /// Count all of the entities beneath an entity in the hierarchy and return the total. The caller should lock the DataModel.
        /// </summary>
        /// <param name="parent">The entity to start from.</param>
        /// <returns>The total number of entities beneath 'entity' in the hierarchy.</returns>
        private int CountChildren(EntityRow parent)
        {
            int count = 0;

            foreach (EntityTreeRow row in parent.GetEntityTreeRowsByFK_Entity_EntityTree_ParentId())
            {
                count += 1;
                count += this.CountChildren(row.EntityRowByFK_Entity_EntityTree_ChildId);
            }

            return(count);
        }
        /// <summary>
        /// Create a new SystemFolder based on an entity row.
        /// </summary>
        /// <param name="entityRow">An entity row in the DataModel.</param>
        public SystemFolder(EntityRow entityRow) : base(entityRow)
        {
            SystemFolderRow systemFolderRow = DataModel.SystemFolder.SystemFolderKey.Find(entityRow.EntityId);

            // Start the application off by opening up the start page.
            string onOpenText = ConfigurationManager.AppSettings["OnOpen"];

            if (onOpenText != null)
            {
                string[] onOpenArguments = onOpenText.Split(new char[] { ',' });
                this.Url = onOpenArguments[2].Trim();
            }
        }