problem deleting a root object

problem deleting a root object

Old forum URL: forums.lhotka.net/forums/t/9112.aspx


Lord02 posted on Monday, June 21, 2010

Hi there,

I'm getting this error while trying to delete inside a UltraGrid ( Infragistics ) bound to a CSLA object:

"Unable to delete the row: invalid for root objects - use Delete instead"

Can you tell me what is happening behind the scenes ... ? What could the Grid be calling inside the CSLA object to get this error message ? Select / Insert / Update works like a charm ... but not delete

I've tried doing it like this ... and this works ... but it ain't pretty !! :-/

 

private void _ugKeyFigureExpenses_BeforeRowsDeleted(object sender, Infragistics.Win.UltraWinGrid.BeforeRowsDeletedEventArgs e)
        {              
            int idToDelete = (Int32)e.Rows[0].Cells["ExpenseID"].Value;

            RecoveryPlanner.Business.KeyFigureExpense.DeleteKeyFigureExpense(idToDelete);
            e.Cancel = true;

            InitializeKeyFiguresTab();   //rebinds the UltraGrid, getting the datasources again from DB
        }    

 

The UltraGrid is abound against a BusinessListBase< KeyFigureExpenseList, KeyFigureExpense >  which is a very simple CSLA class ... I cannot breakpoint into the CSLA, because the UltraGrid gives this error straight away, while hitting the "DEL" on the keyboard.-

( made with the latest CSLA template from CodeSmith v5.2.2 ... http://community.codesmithtools.com/nightly/csla/ )

Please note ... I'm **not** calling "MarkAsChild()" anywhere in the KeyFigureExpense class ( which the KeyFigureExpenseList contains, which is then databound to the Grid ... )
I tried doing it in the constructor but when I call "Save" it will say "cannot call save directly on a child object" when trying to save the object ( insert ).
So I've removed the MarkAsChild, as it was just for testing purposes ... but still not working

Getting:
"Unable to delete the row: invalid for root objects - use Delete instead"

Kind regards,
EE

bniemyjski replied on Monday, June 21, 2010

Hello,

Check your child types. How is delete being called? This error is a result of calling Delete() on the child itself. Make sure the Child's do not have MarkAsChild called any place inside of the entity, especially the partial class.

Thanks

-Blake Niemyjski

Lord02 replied on Monday, June 21, 2010

Hi,

I don't have any children ... I'm not calling "MarkAsChild" anywhere ... I was just trying that for 1 minute too see if that was the problem.
I'm only using ROOT objects, no children.-

I have this in my CSLA class:

public static void DeleteKeyFigureFee(System.Int32 feeID)
        {
            DataPortal.Delete< KeyFigureFee >(new KeyFigureFeeCriteria {FeeID = feeID});
        }

and

protected override void DataPortal_DeleteSelf()
        {
            bool cancel = false;
            OnSelfDeleting(ref cancel);
            if (cancel) return;
           
            DataPortal_Delete(new KeyFigureFeeCriteria (FeeID));
       
            OnSelfDeleted();
        }

 

and

 

[Transactional(TransactionalTypes.TransactionScope)]
        protected void DataPortal_Delete(KeyFigureFeeCriteria criteria)
        {
            bool cancel = false;
            OnDeleting(criteria, ref cancel);
            if (cancel) return;

            using (SqlConnection connection = new SqlConnection(ADOHelper.ConnectionString))
            {
                connection.Open();
                using (SqlCommand command = new SqlCommand("[dbo].[rp_KeyFigureFee_Delete]", connection))
                {
                    command.CommandType = CommandType.StoredProcedure;
                    command.Parameters.AddRange(ADOHelper.SqlParameters(criteria.StateBag));

                    //result: The number of rows changed, inserted, or deleted. -1 for select statements; 0 if no rows were affected, or the statement failed.
                    int result = command.ExecuteNonQuery();
                    if (result == 0)
                        throw new DBConcurrencyException("The entity is out of date on the client. Please update the entity and try again. This could also be thrown if the sql statement failed to execute.");
                }
            }

            OnDeleted();
        }

rgd,
EE

tmg4340 replied on Monday, June 21, 2010

You might not be calling "MarkAsChild" anywhere, but typically an BLB contains child objects - and the DP methods will call "MarkAsChild" for you.  I'm not sure a BLB can contain non-child objects.

What does your BLB Fetch look like?

- Scott

Lord02 replied on Monday, June 21, 2010

Hi,

My BLB are not calling "MarkAsChild" anywhere ... and I'm not getting any CHILD errors ...
I'm getting this error:
"Unable to delete the row: invalid for root objects - use Delete instead"

I'll copy/paste both of my DataAccess layers for the objects here:
remember, my UltraGrid is databound against the KeyFigureFeeList

Here comes the public partial class KeyFigureFeeList : BusinessListBase< KeyFigureFeeList, KeyFigureFee >:

//------------------------------------------------------------------------------
// <autogenerated>
//     This code was generated using CodeSmith: v5.2.2, CSLA Templates: v2.0.1.1719, CSLA Framework: v3.8.2.
//     Changes to this file will be lost after each regeneration.
//     To extend the functionality of this class, please modify the partial class 'KeyFigureFeeList.cs'.
//
//     Template: EditableRootList.DataAccess.StoredProcedures.cst
//     Template website: http://code.google.com/p/codesmith/
// </autogenerated>
//------------------------------------------------------------------------------
#region Using declarations

using System;
using System.Data;
using System.Data.SqlClient;

using Csla;
using Csla.Data;

#endregion

namespace RecoveryPlanner.Business
{
    public partial class KeyFigureFeeList
    {
        [RunLocal]
        protected override void DataPortal_Create()
        {
        }

        private void DataPortal_Fetch(KeyFigureFeeCriteria criteria)
        {
            bool cancel = false;
            OnFetching(criteria, ref cancel);
            if (cancel) return;

            RaiseListChangedEvents = false;

            // Fetch Child objects.
            using (SqlConnection connection = new SqlConnection(ADOHelper.ConnectionString))
            {
                connection.Open();
                using (SqlCommand command = new SqlCommand("[dbo].[rp_KeyFigure_Fees_Select]", connection))
                {
                    command.CommandType = CommandType.StoredProcedure;
                    command.Parameters.AddRange(ADOHelper.SqlParameters(criteria.StateBag));
                    command.Parameters.AddWithValue("@TaskHasValue", criteria.TaskHasValue);
                    command.Parameters.AddWithValue("@InvoiceHasValue", criteria.InvoiceHasValue);
                    command.Parameters.AddWithValue("@AmountHasValue", criteria.AmountHasValue);
                    using(var reader = new SafeDataReader(command.ExecuteReader()))
                    {
                        if(reader.Read())
                        {
                            do
                            {
                                this.Add(new KeyFigureFee(reader));
                            } while(reader.Read());
                        }
                        /*else
                            throw new Exception(string.Format("The record was not found in 'KeyFigure_Fees' using the following criteria: {0}.", criteria));*/
                    }
                }
            }

            RaiseListChangedEvents = true;

            OnFetched();
        }

        protected override void DataPortal_Update()
        {
            bool cancel = false;
            OnUpdating(ref cancel);
            if (cancel) return;

            RaiseListChangedEvents = false;

            for (int index = 0; index < DeletedList.Count; index++)
            {
                DeletedList[index] = DeletedList[index].Save();
            }
          
            DeletedList.Clear();

            for (int index = 0; index < Items.Count; index++)
            {
                Items[index] = Items[index].Save();
            }

            RaiseListChangedEvents = true;

            OnUpdated();
        }
    }
}

 

************************************************************************************************************************************************************
and here comes the Entity itself ............

************************************************************************************************************************************************************

 

//------------------------------------------------------------------------------
// <autogenerated>
//     This code was generated using CodeSmith: v5.2.2, CSLA Templates: v2.0.1.1719, CSLA Framework: v3.8.2.
//     Changes to this file will be lost after each regeneration.
//     To extend the functionality of this class, please modify the partial class 'KeyFigureFee.cs'.
//
//     Template: EditableRoot.DataAccess.StoredProcedures.cst
//     Template website: http://code.google.com/p/codesmith/
// </autogenerated>
//------------------------------------------------------------------------------
#region Using declarations

using System;
using System.Data;
using System.Data.SqlClient;

using Csla;
using Csla.Data;

#endregion

namespace RecoveryPlanner.Business
{
    public partial class KeyFigureFee
    {
        [RunLocal]
        protected override void DataPortal_Create()
        {
            bool cancel = false;
            OnCreating(ref cancel);
            if (cancel) return;

            ValidationRules.CheckRules();

            OnCreated();
        }

        protected void DataPortal_Fetch(KeyFigureFeeCriteria criteria)
        {
            bool cancel = false;
            OnFetching(criteria, ref cancel);
            if (cancel) return;

            using (SqlConnection connection = new SqlConnection(ADOHelper.ConnectionString))
            {
                connection.Open();
                using (SqlCommand command = new SqlCommand("[dbo].[rp_KeyFigure_Fees_Select]", connection))
                {
                    command.CommandType = CommandType.StoredProcedure;
                    command.Parameters.AddRange(ADOHelper.SqlParameters(criteria.StateBag));
                    command.Parameters.AddWithValue("@TaskHasValue", criteria.TaskHasValue);
                command.Parameters.AddWithValue("@InvoiceHasValue", criteria.InvoiceHasValue);
                command.Parameters.AddWithValue("@AmountHasValue", criteria.AmountHasValue);
                    using(var reader = new SafeDataReader(command.ExecuteReader()))
                    {
                        if(reader.Read())
                            Map(reader);
                        else
                            throw new Exception(string.Format("The record was not found in 'KeyFigure_Fees' using the following criteria: {0}.", criteria));
                    }
                }
            }

            OnFetched();
        }

        [Transactional(TransactionalTypes.TransactionScope)]
        protected override void DataPortal_Insert()
        {
            bool cancel = false;
            OnInserting(ref cancel);
            if (cancel) return;

            using (SqlConnection connection = new SqlConnection(ADOHelper.ConnectionString))
            {
                connection.Open();
                using (SqlCommand command = new SqlCommand("[dbo].[rp_KeyFigure_Fees_Insert]", connection))
                {
                    command.CommandType = CommandType.StoredProcedure;
                    command.Parameters.AddWithValue("@FeeID", this.FeeID);
                    command.Parameters["@FeeID"].Direction = ParameterDirection.Output;
                    command.Parameters.AddWithValue("@ProjectID", this.ProjectID);
                    command.Parameters.AddWithValue("@Task", ADOHelper.NullCheck(this.Task));
                    command.Parameters.AddWithValue("@Invoice", ADOHelper.NullCheck(this.Invoice));
                    command.Parameters.AddWithValue("@Amount", ADOHelper.NullCheck(this.Amount));
                    command.ExecuteNonQuery();

                    using (BypassPropertyChecks)
                    {
                        LoadProperty(_feeIDProperty,(System.Int32)command.Parameters["@FeeID"].Value);
                    }
                }

                FieldManager.UpdateChildren(this, connection);
            }

            OnInserted();
        }

        [Transactional(TransactionalTypes.TransactionScope)]
        protected override void DataPortal_Update()
        {
            bool cancel = false;
            OnUpdating(ref cancel);
            if (cancel) return;

            using (SqlConnection connection = new SqlConnection(ADOHelper.ConnectionString))
            {
                connection.Open();
                using (SqlCommand command = new SqlCommand("[dbo].[rp_KeyFigure_Fees_Update]", connection))
                {
                    command.CommandType = CommandType.StoredProcedure;
                    command.Parameters.AddWithValue("@FeeID", this.FeeID);
                    command.Parameters.AddWithValue("@ProjectID", this.ProjectID);
                    command.Parameters.AddWithValue("@Task", ADOHelper.NullCheck(this.Task));
                    command.Parameters.AddWithValue("@Invoice", ADOHelper.NullCheck(this.Invoice));
                    command.Parameters.AddWithValue("@Amount", ADOHelper.NullCheck(this.Amount));
                    //result: The number of rows changed, inserted, or deleted. -1 for select statements; 0 if no rows were affected, or the statement failed.
                    int result = command.ExecuteNonQuery();
                    if (result == 0)
                        throw new DBConcurrencyException("The entity is out of date on the client. Please update the entity and try again. This could also be thrown if the sql statement failed to execute.");

                }

                FieldManager.UpdateChildren(this, connection);
            }

            OnUpdated();
        }

        protected override void DataPortal_DeleteSelf()
        {
            bool cancel = false;
            OnSelfDeleting(ref cancel);
            if (cancel) return;
           
            DataPortal_Delete(new KeyFigureFeeCriteria (FeeID));
       
            OnSelfDeleted();
        }
       
        [Transactional(TransactionalTypes.TransactionScope)]
        protected void DataPortal_Delete(KeyFigureFeeCriteria criteria)
        {
            bool cancel = false;
            OnDeleting(criteria, ref cancel);
            if (cancel) return;

            using (SqlConnection connection = new SqlConnection(ADOHelper.ConnectionString))
            {
                connection.Open();
                using (SqlCommand command = new SqlCommand("[dbo].[rp_KeyFigure_Fees_Delete]", connection))
                {
                    command.CommandType = CommandType.StoredProcedure;
                    command.Parameters.AddRange(ADOHelper.SqlParameters(criteria.StateBag));

                    //result: The number of rows changed, inserted, or deleted. -1 for select statements; 0 if no rows were affected, or the statement failed.
                    int result = command.ExecuteNonQuery();
                    if (result == 0)
                        throw new DBConcurrencyException("The entity is out of date on the client. Please update the entity and try again. This could also be thrown if the sql statement failed to execute.");
                }
            }

            OnDeleted();
        }

        private void Map(SafeDataReader reader)
        {
            bool cancel = false;
            OnMapping(reader, ref cancel);
            if (cancel) return;

            using(BypassPropertyChecks)
            {
                LoadProperty(_feeIDProperty, reader["FeeID"]);
                LoadProperty(_projectIDProperty, reader["ProjectID"]);
                LoadProperty(_taskProperty, reader["Task"]);
                LoadProperty(_invoiceProperty, reader["Invoice"]);
                LoadProperty(_amountProperty, reader["Amount"]);
            }

            OnMapped();

            MarkOld();
        }
    }
}

tmg4340 replied on Monday, June 21, 2010

Lord02

Hi,

My BLB are not calling "MarkAsChild" anywhere ... and I'm not getting any CHILD errors ...
I'm getting this error:
"Unable to delete the row: invalid for root objects - use Delete instead"

That's my point - you're getting root errors.  BusinessListBase is designed to contain child objects, not root objects.  So your KeyFigureFeeList collection is attempting to interact with the KeyFigureFee objects in its collection as if they were child objects.  What is happening is that the UltraGrid's delete calls are getting to the "DeleteChild" method on your KeyFigureFee object.  Since the object is not marked as a child, it's throwing the exception you're seeing.

You either need to re-work your KeyFigureFee object so that it's a child object (and can be contained within a BusinessListBase-derived object), or you need to change KeyFigureFeeList to an EditableRootListBase-derived collection (which will change how the collection interacts with your grid in significant ways that you need to be aware of.)

HTH

- Scott

Lord02 replied on Monday, June 21, 2010

Hi again,

I'm not at work now ( it's 00:37 here ... ) but I will check this out tomorrow

What are these "significant" other ways the UltraGrid would interact if I'd change to EditableRootListBase ?
- Would you recommend that approach ?

My KeyFigureList ( and KeyFigureFee entity )  do not contain any children ... nor are they children themselfs, so I'm not liking the idea of marking something as MarkAsChild() .            They are ROOTS.
( I have a form ... combined with multiple Root Objects ) ... they all currently work for select / insert and update ... but NOT delete.-

Please advice ?

Thanx in advance,
EE

tmg4340 replied on Monday, June 21, 2010

Making your KeyFigureList an ERLB will essentially remove the "collection behavior" of the list - i.e. submitting all changes at once.  The ERLB was designed for a specific, grid-centric purpose - having a collection of root objects displayed in a grid that act independently.  The biggest one is that the DP_ methods are called immediately after the object "loses focus" in the grid.  Rocky has outlined that in several places here in the forum, so if you do a search you should find all the particulars.

If your KeyFigure objects are contained within a KeyFigureList, then they are child objects, whether you think about them that way or not.  As I said, that's how BLB is designed to work.  Your KeyFigureList collection is your root object, and your KeyFigureList objects are children of that root.  If your KeyFigure objects aren't children of your KeyFigureList collection, then why are they in it?

In any event, without knowing more about what you're trying to do with your UI (and what UI tech you're using), it's hard to make much of a recommendation.

- Scott

Copyright (c) Marimer LLC