Link to home
Start Free TrialLog in
Avatar of sachiek
sachiekFlag for Singapore

asked on

Master child records in one table , treeview struct, deletion algorithm

Hi All,
  I am looking forward for some good algorithm for one of my requirement.

 I got a table which will be displayed in frontend in treeview.
 So basically records will be navigated through child with parent id.
 When building the tree it starts from first record. Then start building one by one with by category which represent which level current node to be built.

 Now query here is i want a effective algorithm to delete those child records when parent record is deleted.
 
  My table will be having
  ID, Name, ParentID, CategoryID Details, etc...
  1     Testing, null, ....., 1
  2     Child1 ,  1 , .....  , 2
  3     Testing1, null, .......,1
  4     Child2   , 3 , ..........,2
 
  Above is sample records. But tree node depth will go down to 8 levels. So I cannot assume or hardcode any values.
  I want to have a effective algoritm to delete those child nodes from table when deleting parent.
  And that deletion should be genric enough to delete at any level. Like if i want to delete just the child alone then also it should be possible.
  Let me know if any further details needed.
  Looking for it.

Cheers!
Sachi

 
Avatar of davidastle
davidastle

// Let me take stab a this.  I'm not exactly sure what you are asking, but i think it is that you
// want a tree and a table, and when you delete a node from the tree, it will delete all the child
// nodes from both that tree and the table.
// So to that, i made a tree node class.  When you make the node, pass in the id.
// Your program can subscribe to the Delete event in each node, and add a callback function that
// will in turn delete the row with the deleted nodes id.

using System;
using System.Windows.Forms;

namespace MyNameSpace
{

      class TreeNodeDerived : TreeNode
      {
            private int myId;
            public delegate void DeleteCallback(int deletedNodeID);
            public event DeleteCallback Delete;
            public TreeNodeDerived(int ID)
            {
                  myId = ID;
            }
            public void DeleteNodeAndChildren()
            {
                  foreach (TreeNodeDerived currentNode in this.Nodes)
                        currentNode.DeleteNodeAndChildren();
                  // remove this node or add your own delete implementation
                  this.OnDelete();
            }
            protected void OnDelete()
            {
                  this.Remove();
                  Delete(myId);
            }
            public int ID
            {
                  get
                  {
                        return myId;
                  }
            }
      }
}
Avatar of sachiek

ASKER

Sorry David. I think you took it different.
Everytime I delete a node or soo, I will be refreshing whole treeview. so that I get updated treeview.

But I want to delete those records from table. My point here will be to delete those records from table.

Treeview I do not have any problem.

Cheers!
Sachi

One posible solution is using a recursive method to gather in a collection the ID's to delete (easier to write but not the most efficient) .

ArrayList aID = new ArrayList();
aID.Add(firstID); // first you add the parent node ID - the one you want to delete
RecDelete(firstID); // call the recursive procedure to identify the IDs which are to be deleted

private void RecDelete(int myID)
{
      for (int i=0;i<MyTable.Rows.Count;i++)
      {
            if (!MyTable.Rows[i].IsNull("ParentID"))
                        // if the ParentID is equal with parameter add the to collection and
                        //  recursively call the procedure for the added ID
                  if ((int)MyTable.Rows[i]["ParentID"]==myID)
                  {
                        aID.Add((int)MyTable.Rows[i]["ID"]);
                        RecDelete((int)MyTable.Rows[i]["ID"]);
                  }
      }
}

Now you have to scan the table one more time and delete the rows which ID's are in the collection.

Note: you cannot delete the records directly because it is posible to skip rows when you scan the table multiple times.

hth
This is an issue I recently ran into and solved.  Here is the high level of what I did...

1) Create a class for the data, or multiple classes if needed.  Inherit from a GenericClass ( I called mine DataClassBase ).

2) Create a strongly typed collection for the classes created in step 1, which inherit from a GenericClassCollection ( I called mine DataClassCollection) that inherits from CollectionBase (DotNET abstract class).

3) On the DataClassBase include three boolean properties IsNew, IsDirty, ShouldDelete.  Also, for your case create a property that is of DataClassCollection.

4) On the DataClassBase include a public virtual Load( IDataReader dr ) {} and public virtual Save() {} plus whatever else you need.  Note these methods will need to be overridden for each DataClassBase.  Also for you case create a public virtual Delete() {}, which will simply set the ShouldDelete property to true and if the DataClassCollection property is not null loop throught that calling each of those classes delete method.  Note the save will also need to loop through the DataClassCollection.

5) On the DataClassCollection include methods for loading the collection and saving the collection.  Note, my load got the data from the DB, positioned the record pointer to a record and then called the DataClassBase load method.  The save collection loops through contained classes calling the DataClassBase save method.

6) When a node is set to be deleted call the associated classes delete method.

7) When apply is hit (or what ever you want to trigger it) call the Save on the top DataClassCollection.

Obviously, you need to make sure you keep the connection between the node in the tree and the class in the collection.

By doing it this way you only have to load one layer at a time, i.e load the first collection then only load a subcollection when the node is expanded.  All of the work is done in a business layer as opposed to the UI layer.  A great deal of the work if generic enough can be done in the generic classes so that the children classes simply use the methods, which makes adding classes easier.  The DataClasses can be used to translate the data to look like you want, for example if I have Id, FName and LName in a class but I want the tree node to look like ID : Full Name then I can create a property like public string TNode { get { return Id + ": " + fName + " " + lName; } }.  If I do this on all of my data classes then it does not matter what the data is in the class I simply use the TNode property to get what I want to display in the tree.

Anyhow, if you are interested in this I can give you some sample code.

nipnfriar_tuck
Avatar of sachiek

ASKER

Hi Nipnfriar_tuck,
   Sorry for delay in reply. I was working on other things.
   So your explaintation gives me a small picture.
  Ya I would be better to view a sample.
  But wondering whether it is possible to do it still more simpler. Have a class will solve this issue means..anyway lets see your example.
  I only intrested in delete method. Other things i do not need.
 
  Currently I am using a exhaustive way which do not give me full result i want too.
  Hope to solve this sooner.
  Well, you are understand the whole deletion part correctly right.
  When deleting parent all the child records should be deleted. i.e even if child have sub-childs too. So deleting child again need to delete sub-child too. Hope your did acheived it with your logic already :) ..

  Looking for your reply.

Thanks and Regards,
Sachi
 
Alrighty then here you go....

Lets say you have a dataclass defined like:

using System;
using System.Data;
using System.Collections;

namespace MyCompany.MyDomain.MyNamespace
{
      public class DataClassBase : IDisposable
      {
            #region Fields
            protected bool isNew;
            protected bool isDirty;
            protected bool shouldDelete;

            protected string id = String.Empty;
            protected string name = String.Empty;
            protected string description=String.Empty;
            #endregion

            #region Properties
            public bool IsNew
            {
                  get { return isNew; }
                  set { isNew = value; }
            }
            public bool IsDirty
            {
                  get { return isDirty; }
                  set { isDirty = value; }
            }
            public bool ShouldDelete
            {
                  get { return shouldDelete; }
                  set { shouldDelete = value; }
            }


            public string ID
            {
                  get { return id; }
                  set { id = value; }
            }
            public string Name
            {
                  get { return name; }
                  set { name = value; isDirty=true; }
            }

            public string Description
            {
                  get { return description;}
                  set { description = value;
                              isDirty=true;
                        }
            }
            #endregion

            #region Base
            public SecClassBase()
            {
                  IsNew = true;
                  IsDirty = false;
                  ShouldDelete = false;
            }
            #region IDisposable Members

            public void Dispose()
            {
                  
            }

            #endregion
            #endregion

            #region Methods
            public virtual void Load( IDataReader dr ) {}
            public virtual void Save( SecDataAccess DA ) {}
            public virtual System.Windows.Forms.TreeNode Populate()
            {
                  System.Windows.Forms.TreeNode treeNode = new System.Windows.Forms.TreeNode();
                  treeNode.Text=this.Name;
                  treeNode.Tag=this.ID;
                  return treeNode;
            }

            public virtual System.Windows.Forms.ListViewItem Populate(int index)
            {
                  System.Windows.Forms.ListViewItem lvItem = new System.Windows.Forms.ListViewItem();
                        lvItem.Text=this.Name;
                  lvItem.Tag=this.ID;
                  lvItem.SubItems[lvItem.SubItems.Count - 1].Text= index.ToString();
                  return lvItem;
            }
            public virtual void Define( ref System.Windows.Forms.ListView lv, System.Windows.Forms.HorizontalAlignment ha  ){}

            #endregion
      }

I would also create a collection class...
      public class DataClassCollection : CollectionBase, IDisposable
      {
            #region Collection Methods

            protected override void OnValidate( Object value )  
            {
                  if ( value.GetType().Name != "DataClassBase")
                        throw new ArgumentException( "value must be of type DataClassBase.", "value" );
            }

            public DataClassBase this[int index]
            {
                  get
                  {
                        if ( index >= 0 && index < List.Count )
                        {
                              return((DataClassBase)List[index]);
                        }
                        else
                        {
                              return null;
                        }
                  }
                  set
                  {
                        if ( index >= 0 && index < List.Count )
                        {
                              List[index] = value;
                        }
                  }
            }
      
            public int Add(DataClassBase dataClassBase)
            {
                  int index = List.Add(dataClassBase);
                  return index;
            }
            public void Remove(DataClassBase dataClassBase)
            {
                  List.Remove(dataClassBase);
            }
            public void Insert( int index, DataClassBase value )  
            {
                  List.Insert( index, value );
            }

            public int IndexOf( DataClassBase value )  
            {
                  return( List.IndexOf( value ) );
            }
            #endregion

            #region PublicMethods

            /// <summary>
            /// This is base class method which Loads the Collection with all the Items.  
            /// </summary>
            public virtual void LoadObjects(){}

            /// <summary>
            /// This is Base class method
            /// This method Loops through all the Items in the collection and perform the
            /// Save action. Item can marked for ShouldDelete/IsDirty/IsNew. Based upon
            /// whats if the flag type action is taken.
            /// ShouldDelete := Item is Removed from the Collection and from database
            /// IsDirty      := Item is updated for changes made
            /// IsNew        := New item is added to collection and to database.
            /// </summary>
            public virtual void SaveObjects(){}

            /// <summary>
            ///
            /// </summary>
            /// <param name="Id"></param>
            /// <returns></returns>
            public virtual DataClassCollection Filter( string Id ) { return null; }

            /// <summary>
            ///
            /// </summary>
            /// <param name="Uid"></param>
            /// <returns></returns>
            public virtual DataClassCollection Filter( int Uid ,bool isUser ) { return null; }


            /// <summary>
            ///
            /// </summary>
            /// <param name="parentNode"></param>
            /// <param name="lastLevel"></param>
            public virtual void Populate(ref System.Windows.Forms.TreeNode parentNode, bool lastLevel)
            {
                  for ( int i = 0; i < List.Count; i++ )
                  {
                        System.Windows.Forms.TreeNode tNode = this[i].Populate();
                        if ( !lastLevel )
                        {
                              tNode.Nodes.Add( new System.Windows.Forms.TreeNode() );
                        }
                        parentNode.Nodes.Add( tNode );
                  }
            }

            /// <summary>
            ///
            /// </summary>
            /// <param name="rootNode"></param>
            /// <param name="lastLevel"></param>
            public virtual void Populate(ref System.Windows.Forms.TreeView rootNode, bool lastLevel)
            {
                  for ( int i = 0; i < List.Count; i++ )
                  {
                        System.Windows.Forms.TreeNode tNode = this[i].Populate();
                        if ( !lastLevel )
                        {
                              tNode.Nodes.Add( new System.Windows.Forms.TreeNode() );
                        }
                        rootNode.Nodes.Add( tNode );
                  }
            }


            /// <summary>
            ///
            /// </summary>
            /// <param name="lvw"></param>
            public virtual void Populate(ref System.Windows.Forms.ListView lvw)
            {
                  
                  for ( int i = 0; i < List.Count; i++ )
                  {
                        lvw.Items.Add( this[i].Populate(i) );
                  }
                  
            }
            #endregion

            #region IDisposable Members

            public void Dispose()
            {
                  if ( List != null )
                  {
                        foreach ( object obj in List )
                        {
                              DataClassBase c = obj as DataClassBase;
                              if ( c != null )
                              {
                                    c.Dispose();
                              }
                        }
                        List.Clear();
                  }
            }

            #endregion
      }
}

With the Base Classes done I would create classes inherited from the base class...

using System;
using System.Data;
using System.Collections;

namespace MyCompany.MyDomain.MyNamespace
{
      public class MyDataClass: DataClassBase
      {
            #region Fields
            private string myDataUid;
            private string myDataText;
            #endregion

            #region Properties

            public string MyDataUid
            {
                  get{return myDataUid;}
                  set{myDataUid=value;isDirty=true;}
            }
            public string MyDataText
            {
                  get{return myDataText;}
                  set{myDataText=value;isDirty=true;}
            }
            #endregion

            #region Base
            public SecRoleObjectRights()
            {
            }
            #endregion

            #region Public Methods

            /// <summary>
            /// This method load a single Item the class
            /// </summary>
            /// <param name="dr"></param>
            public override void Load( IDataReader dr )
            {
                  this.myDataUid=OracleDataAccess.GetReaderString( dr, "MyData_UID" );
                  this.MyDataText=OracleDataAccess.GetReaderString( dr, "MyData_Text" );
            }
      

            /// <summary>
            /// This method is for Saving individual Item of the Class.
            /// Also this method is called form SecRoleObjectRightsCollection
            /// which help to load the SecRoleObjectRightsCollection
            /// </summary>
            public override void Save(  )
            {
                  if (shouldDelete)
                  {
                        // Delete the record
                  }
                  else if ( isDirty || isNew )
                  {
                        if ( isNew ) {
                                   // Insert record
                                }
                                else {
                                   // Update record
                                }
                  }
            }

            /// <summary>
            /// This method Adds Columns to the Listview object, which is passed as parameter
            /// </summary>
            /// <param name="lv">Listview Object</param>
            /// <param name="ha">Horizontal Position</param>
            public override void Define( ref System.Windows.Forms.ListView lv, System.Windows.Forms.HorizontalAlignment ha )
            {
                  lv.Columns.Add("My Data Id", 96, ha);
                  lv.Columns.Add("My Data Text", 96, ha);
            }

            /// <summary>
            /// This method Creates new ListViewItem and returns , so that the ListView object can be populated
            /// </summary>
            /// <param name="index"></param>
            /// <returns>ListViewItem</returns>
            public override System.Windows.Forms.ListViewItem Populate(int index)
            {
                  System.Windows.Forms.ListViewItem lvi = new System.Windows.Forms.ListViewItem( this.MyDataUid );
                  lvi.Tag = this.MyDataUid;
                  lvi.SubItems.Add( this.MyDataText );
                  return lvi;
            }
            
            #endregion
      }

      public class MyDataCollection: DataClassCollection, IDisposable
      {
            #region Collection Methods

            protected override void OnValidate( Object value )  
            {
                  if ( value.GetType().Name != "MyDataClass")
                        throw new ArgumentException( "value must be of type MyDataClass.", "value" );
            }

            public new MyDataClass this[int index]
            {
                  get
                  {
                        if ( index >= 0 && index < List.Count )
                        {
                              return((MyDataClass)List[index]);
                        }
                        else
                        {
                              return null;
                        }
                  }
                  set
                  {
                        if ( index >= 0 && index < List.Count )
                        {
                              List[index] = value;
                        }
                  }
            }
      
            public int Add(MyDataClass myDC)
            {
                  int index = List.Add(myDC);
                  return index;
            }
            public void Remove(MyDataClass myDC)
            {
                  List.Remove(myDC);
            }
            public void Insert( int index, MyDataClass value )  
            {
                  List.Insert( index, value );
            }

            public int IndexOf( MyDataClass value )  
            {
                  return( List.IndexOf( value ) );
            }
            #endregion

            #region Public Methods

            /// <summary>
            /// Loads the MyDataCollection with all the Items.  
            /// </summary>
            public override void LoadObjects()
            {
                  List.Clear();

                  IDataReader Reader = null;      

                  // Get the data into the reader...

                  while( true == Reader.Read() )
                  {
                        MyDataClass dc = new MyDataClass();
                        dc.Load( Reader );
                        this.Add(r);
                  }
                  Reader = null;
            }

            /// <summary>
            /// This method Loops through all the Items in the collection and perform the
            /// Save action. Item can marked for ShouldDelete/IsDirty/IsNew. Based upon
            /// whats if the flag type action is taken.
            /// ShouldDelete := Item is Removed from the Collection and from database
            /// IsDirty      := Item is updated for changes made
            /// IsNew        := New item is added to collection and to database.
            /// </summary>
            public override void SaveObjects( )
            {
                  ArrayList exCol = new ArrayList();
                  foreach ( MyDataClass u in this )
                  {
                        try
                        {
                              u.Save();
                        }
                        catch ( Exception ex )
                        {
                              exCol.Add( ex );
                        }
                  }
                  if ( exCol.Count > 0 )
                  {
                        string exMsg = "";
                        foreach ( Exception ex in exCol )
                        {
                              exMsg += ex.Message + "; ";
                        }
                        throw new Exception( exMsg );
                  }
            }
            
            public override MyDataCollection Filter( string Id )
            {
                  MyDataCollection col = new MyDataCollection();
                  for ( int i = 0; i < this.Count; i++ )
                  {
                        MyDataClass su = this[i] as MyDataClass;
                        if ( su != null && su.MyDataUid.Equals( Id ) )
                        {
                              col.Add( su );
                        }
                  }
                  return col;
            }

            #endregion

            #region IDisposable Members

            public new void Dispose()
            {
                  if ( List != null )
                  {
                        foreach ( object obj in List )
                        {
                              MyDataClass c = obj as MyDataClass;
                              if ( c != null )
                              {
                                    c.Dispose();
                              }
                        }
                        List.Clear();
                  }
            }

            #endregion
      }
}

Finally, you use these classes in a tree like this:

You would have methods like this in the class...

            #region Company
            
            private void FillCompanyTree()
            {
                  treeViewCompanies.Nodes.Clear();
                  MyDataCollection sccol = new MyDataCollection();
                  sccol.LoadObjects( );
                  sccol.Populate( ref treeViewCompanies, false );
            }
            private void treeViewCompanies_AfterSelect(object sender, System.Windows.Forms.TreeViewEventArgs e)
            {
                  try
                  {
                        string[] nodes = e.Node.FullPath.Split( '\\' );
                        int nodeValue=int.Parse(e.Node.Tag.ToString());
                        if ( nodes.Length > 3 )
                        {
                              bool isUser = false;
                              foreach ( string n in nodes )
                              {
                                    if ( n.Equals("Users") )
                                    {
                                          isUser = true;
                                          break;
                                    }
                              }
                              string nodeName = nodes[nodes.Length - 1];
                              PopulateCompanyListView(nodeValue,isUser);
                        }                        
                  }
                  catch( Exception Err )
                  {
                        ReportErr( Err );
                  }
            }

            private void treeViewCompanies_BeforeExpand(object sender, System.Windows.Forms.TreeViewCancelEventArgs e)
            {
                  TreeNode tNode = e.Node;
                  string[] nodes = e.Node.FullPath.Split( '\\' );
                  switch ( nodes.Length )
                  {
                        case 1:
                              FillNode(new MyDataCollection(), tNode.Tag.ToString(), false, ref tNode);
                              break;
                        case 2:
                              tNode.Nodes.Clear();
                              TreeNode uNode = new TreeNode();
                              uNode.Text = "Users";
                              uNode.Tag = "Users";
                              uNode.Nodes.Add( new TreeNode() );
                              tNode.Nodes.Add( uNode );
                              TreeNode wNode = new TreeNode();
                              wNode.Text = "Workstations";
                              wNode.Tag = "Workstations";
                              wNode.Nodes.Add( new TreeNode() );
                              tNode.Nodes.Add( wNode );
                              break;
                        default:
                              string nodeName = nodes[nodes.Length - 1];
                        switch ( nodeName )
                        {
                              case "Users":
                                    FillNode(new MyDataCollection(), tNode.Parent.Tag.ToString(), true, ref tNode);
                                    break;
                              case "Workstations":
                                    FillNode(new MyDataCollection(), tNode.Parent.Tag.ToString(), true, ref tNode);
                                    break;
                        }
                              break;
                  }
            }
            private void PopulateCompanyListView(string Id)
            {
                  
                  System.Windows.Forms.Cursor formerCursor = this.Cursor;
                  this.Cursor = System.Windows.Forms.Cursors.WaitCursor;
                  try
                  {
                        MyDataCollection sur = new MyDataCollection();
                        sur.LoadObjects( );
                        viewUWRCol = (MyDataCollection)sur.Filter(Id);
                        FillDataView( viewUWRCol, ref listViewCOMP );
                        this.statusBarPanelRpt.Text = "";
                  }
                  catch( Exception Err )
                  {
                        EH.PerformCatch(Err);
                        this.statusBarPanelRpt.Text = "ERROR: " + Err.Message;
                  }
                  finally
                  {                        
                        
                        this.Cursor = formerCursor;
                  }
            }

            private void PopulateCompanyListView(int Id, bool isUser)
            {
                  
                  System.Windows.Forms.Cursor formerCursor = this.Cursor;
                  this.Cursor = System.Windows.Forms.Cursors.WaitCursor;
                  try
                  {
                        MyDataCollection sur = new MyDataCollection();
                        sur.LoadObjects(  );
                        viewUWRCol = (MyDataCollection)sur.Filter(Id, isUser);
                        FillDataView( viewUWRCol, ref listViewCOMP );
                        this.statusBarPanelRpt.Text = "";
                  }
                  catch( Exception Err )
                  {
                        EH.PerformCatch(Err);
                        this.statusBarPanelRpt.Text = "ERROR: " + Err.Message;
                  }
                  finally
                  {                        
                        
                        this.Cursor = formerCursor;
                  }
            }


            private void FillNode( MyDataCollection scc, string id, bool lastLevel, ref TreeNode ParentNode )
            {
                  scc.LoadObjects(  );
                  ParentNode.Nodes.Clear();
                  scc.Filter( id ).Populate( ref ParentNode, lastLevel );
            }
            
            private void NewUWR()
            {
                  try
                  {
                        if ( DlgEditComp == null )
                        {
                              DlgEditComp = new DlgSecurityEditUserRole( SEC, DA );
                        }
                        // pop up the record dialog
                        DlgEditComp.PopulateScreen( );
                        DlgEditComp.FormParent = this;
                        DlgEditComp.AlignTo(-1,-1,-1,-1,false,System.DateTime.Parse("1/1/1800"),System.DateTime.Parse("1/1/1800"));
                        DlgEditComp.Closed += new EventHandler(DlgEditComp_Closed);
                        DlgEditComp.ShowDialog( this );
                  }
                  catch( Exception Err )
                  {
                        ReportErr( Err );
                  }
            }
            private void UpdateUWR()
            {
                  try
                  {
                        if ( 1 == this.listViewCOMP.SelectedItems.Count)
                        {
                              System.Windows.Forms.ListViewItem LVI = this.listViewCOMP.SelectedItems[0];

                              // pop up the record dialog
                              DlgEditComp.PopulateScreen( );
                              // set the possibilities for the record we are editing
                              string[] aIds = ((string)LVI.Tag).Split(new char[] { ' ' } );

                              System.DateTime dEff = System.DateTime.Parse( "1/1/1800" );
                              if ( "" != LVI.SubItems[5].Text )
                              {
                                    dEff = System.DateTime.Parse( LVI.SubItems[5].Text );
                              }
                              System.DateTime dExp = System.DateTime.Parse( "1/1/1800" );
                              if ( "" != LVI.SubItems[6].Text )
                              {
                                    dExp = System.DateTime.Parse( LVI.SubItems[6].Text );
                              }

                              string ndx = listViewCOMP.SelectedItems[0].SubItems[listViewCOMP.SelectedItems[0].SubItems.Count-1].Text;
                              int index = int.Parse(ndx);
                              secUWR = viewUWRCol[index];
                              secUWR.IsNew = false;
                              DlgEditComp.AlignTo( secUWR );
                              DlgEditComp.FormParent = this;

                              DlgEditComp.Closed += new EventHandler(DlgEditComp_Closed);
                              DlgEditComp.Show( );
                        }
                  }
                  catch( Exception Err )
                  {
                        ReportErr( Err );
                  }
            }

            private void DeleteUWR()
            {
                  try
                  {
                        string ndx = listViewCOMP.SelectedItems[0].SubItems[listViewCOMP.SelectedItems[0].SubItems.Count-1].Text;
                        int index = int.Parse(ndx);
                        secUWR = viewUWRCol[index];
                        secUWR.ShouldDelete = true;
                        secUWR.Delete( DA );
                        PopulateCompanyListView(treeViewCompanies.SelectedNode.Text);
                  }
                  catch( Exception Err )
                  {
                        ReportErr( Err );
                  }
            }
            #endregion


Anyhow, this is code that we are actively using, except that I have change the sensitive parts ... i.e. data connection et al.  This code is very fast and reactive on the UI and has allowed a great deal of re-use...  If I get the time I will try to put together a working solution, but that will not be anytime soon.


Regards.
Avatar of sachiek

ASKER

Oh i c. So you are using this for winforms.
Anyway let me take a look and update you.

Sachi
Avatar of sachiek

ASKER

Hi,
  I am not really satisfied to go with your class for now. Because I want to make this still more simplier.

  Here below i am sharing my logic. I am looking forward to optimize this 100%.

  method DeleteMain (id, parentid, categoryid)
  {

           Reason is that i can check all the levels after given categoryid.
           Example... if category id was given as 4, then drill down from 4 to 8.
           So for each category i will call a seperate function.       

           Switch(categoryid)
           {
                Call the methods accordingly.
        //methods will be like cat3, cat4 etc......
           }
  }
 
 method cat3(ds)
 {
     Get through each row in dataset.
      {
            check if any immediate child is avilable.
                  if soo invoke cat4 method passing on that ds
            else
                  simply delete the record
      }
}

 method cat4(ds)
 {
     Get through each row in dataset.
      {
            check if any immediate child is avilable.
                  if soo invoke cat5 method passing on that ds
            else
                  simply delete the record
      }
}

 method cat5(ds)
 {
     Get through each row in dataset.
      {
            check if any immediate child is avilable.
                  if soo invoke cat6 method passing on that ds
            else
                  simply delete the record
      }
}

 method cat6(ds)
 {
     Get through each row in dataset.
      {
            check if any immediate child is avilable.
                  if soo invoke cat7 method passing on that ds
            else
                  simply delete the record
      }
}


 method cat7(ds)
 {
     Get through each row in dataset.
      {
            check if any immediate child is avilable.
                  if soo invoke cat8 method passing on that ds
            else
                  simply delete the record
      }
}


 method cat8(ds)
 {
     Get through each row in dataset.
      {
            check if any immediate child is avilable.
                  if soo invoke cat8 method passing on that ds
            else
                  simply delete the record
      }
}



Well, something i am missing. I think this logic can be seriously simplified.


Cheers!
Sachi


Hi,
I still consider that a recursive method is the simplest way to solve the problem. The method will receive an ID as parameter, will scan the collection of rows and if encounters a row whose parentID is equal with the parameter will delete the row and it will call itself giving the rowID as the new parameter.

For the given example
  ID, Name, ParentID, CategoryID Details, etc...
  1     Testing, null, ....., 1
  2     Child1 ,  1 , .....  , 2
  3     Testing1, null, .......,1
  4     Child2   , 3 , ..........,2
if I delete the first row I will call the method with parameter ID=1, RecDelete(1), the value 1 will be find in ParentID column on the second row and the method will be called with parameter ID=2, RecDelete(2). The second call will delete nothing because value 2 is not found on ParentID column.

Actually you can not delete the records directly, you have to collect the IDs first and delete the records  after you have returned from the recursive method.

cheers


I agree with sumix on using a recursive method, that is if you are not going to use the strongly typed/object concept that I stated earlier.
Avatar of sachiek

ASKER

Hi,
  I had solved it with a trigger, and it was a different choice.
  Put a colum to track parent and those child nodes under each parent.

  It means i will be able to retreive all those child node when i give "1.%" means all those records i can get it. If I want only to child level then it mean i will retrive with sql statment as "1.1.%" it means i will get those nodes which are under child first level.

  I guess so this question can be deleted.  

  Thanks anyway for all of your support.

Regards,
Sachi
ASKER CERTIFIED SOLUTION
Avatar of Computer101
Computer101
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial