/// <summary>
        /// TRANSACTIONS INSIDE.
        /// Execute all operators in three steps.
        /// Also checks host problem, if any, fix and prompt user
        /// to restart operation.
        /// </summary>
        public static void Operation123(Document _doc, IEnumerable <ElementOperator> _ops)
        {
            var    removers    = _ops.Where(x => x is ElementRemover);
            var    nonRemovers = _ops.Where(x => x is ElementRemover == false);
            var    ds          = new DialogSuppressor();
            string errorMsg    = "操作未完成。";

            using (Transaction trans = new Transaction(_doc, "preprocess"))
            {
                ds.UseInTransaction(trans);
                trans.Start();
                foreach (var op in nonRemovers)
                {
                    op.PreProcess();
                }
                trans.Commit();
                if (trans.GetStatus() != TransactionStatus.Committed)
                {
                    throw new TransactionNotCommitedException(errorMsg);
                }
            }
execute:
            using (Transaction trans = new Transaction(_doc, "execute"))
            {
                ds.UseInTransaction(trans);
                trans.Start();
                foreach (var op in nonRemovers)
                {
                    if (!op.Cancel)
                    {
                        op.Execute();
                    }
                }
                if (FamilyCreator.HostsWithInstanceFaceProblem.Count > 0)
                {
                    trans.RollBack();
                }
                else
                {
                    trans.Commit();
                    if (trans.GetStatus() != TransactionStatus.Committed)
                    {
                        throw new TransactionNotCommitedException(errorMsg);
                    }
                }
            }

            //the geometry face retrieved from it might be in one of two states:
            //A, directly retrieved from instance. The stable reference string does not contain "INSTANCE", or;
            //B, retrieved from symbol geometry first, then apply instance transform. The stable reference string contains "INSTANCE".
            //State B might result in failure of creating new instance on face, even when the line stays
            //perfectly inside face.
            //To avoid this problem, a face with state A must be ensured.
            //A join operation of the host will make sure that faces retrieve from it
            //will always be in state A.
            if (FamilyCreator.HostsWithInstanceFaceProblem.Count > 0)
            {
                UserMessages.ShowMessage("某些主体图元导致本次操作失败。已修复这些图元,请重新进行操作。");
                using (Transaction trans = new Transaction(_doc, "fix"))
                {
                    ds.UseInTransaction(trans);
                    trans.Start();
                    foreach (var host in FamilyCreator.HostsWithInstanceFaceProblem)
                    {
                        HostUtils.FixInstanceFaceProblem(host);
                    }
                    trans.Commit();
                }
            }
            FamilyCreator.HostsWithInstanceFaceProblem.Clear();

            using (Transaction trans = new Transaction(_doc, "postprocess"))
            {
                ds.UseInTransaction(trans);
                trans.Start();
                foreach (var op in nonRemovers)
                {
                    if (!op.Cancel)
                    {
                        op.PostProcess();
                    }
                }
                trans.Commit();
                if (trans.GetStatus() != TransactionStatus.Committed)
                {
                    throw new TransactionNotCommitedException(errorMsg);
                }
            }

            //hand and facing need second round of set.
            var ctrs = nonRemovers
                       .Where(x => x is WallBasedFamilyCreator ||
                              x is FaceBasedFamilyCreator).ToList();

            if (ctrs.Count > 0)
            {
                using (Transaction trans = new Transaction(_doc, "postprocess 2"))
                {
                    ds.UseInTransaction(trans);
                    trans.Start();
                    foreach (var ctr in ctrs)
                    {
                        if (!ctr.Cancel)
                        {
                            ctr.PostProcess();
                        }
                    }
                    trans.Commit();
                    if (trans.GetStatus() != TransactionStatus.Committed)
                    {
                        throw new TransactionNotCommitedException(errorMsg);
                    }
                }
            }

            //delete elements
            using (Transaction trans = new Transaction(_doc, "delete"))
            {
                trans.Start();
                foreach (var op in removers)
                {
                    op.Execute();
                }
                trans.Commit();
            }
        }