DataAccessAdapter events

Posts   
 
    
omar avatar
omar
User
Posts: 569
Joined: 15-Oct-2004
# Posted on: 02-Dec-2005 17:29:09   

LLBL's reference explains the adapter's OnSaveEntityComplete and OnDeleteEntityComplete as methods that are

Called right after the actual Save/Delete action was executed.

1- Is this also true even if I am saving/deleting the entity inside a transaction? if Yes, are these methods called AFTER the OnAfterTransactionCommit method or before it?

2- If the adapter's transaction includes several actions (one or more SaveEntity actions and/or one or more DeleteEntity actions); does comitting the adapter also calls (for each SaveEntity and DeleteEntity action) the OnSaveEntity, OnSaveEntityComplete, OnDeleteEntity, OnDeleteEntityComplete methods?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39618
Joined: 17-Aug-2003
# Posted on: 02-Dec-2005 17:48:47   

omar wrote:

LLBL's reference explains the adapter's OnSaveEntityComplete and OnDeleteEntityComplete as methods that are

Called right after the actual Save/Delete action was executed.

1- Is this also true even if I am saving/deleting the entity inside a transaction? if Yes, are these methods called AFTER the OnAfterTransactionCommit method or before it?

Yes called also if inside transaction, No, called before Onaftertransaction commit.: (from PersistQueue)


// flag we're going to save an entity
OnSaveEntity(saveQuery, entityToSave);

// execute the query
saveSucceeded = (ExecuteActionQuery(saveQuery) > 0);

// flag save action was completed
OnSaveEntityComplete(saveQuery, entityToSave);

2- If the adapter's transaction includes several actions (one or more SaveEntity actions and/or one or more DeleteEntity actions); does comitting the adapter also calls (for each SaveEntity and DeleteEntity action) the OnSaveEntity, OnSaveEntityComplete, OnDeleteEntity, OnDeleteEntityComplete methods?

Yes, those are called for each entity being actually processed. So for every entity which is actually saved or deleted, those methods are called. The same as before.

Frans Bouma | Lead developer LLBLGen Pro
omar avatar
omar
User
Posts: 569
Joined: 15-Oct-2004
# Posted on: 05-Dec-2005 13:02:57   

I want to simulate audit logic that simply calls SaveEntity on the audit entity. Is it possible to override the OnSaveEntity, OnDeleteEntity methods in a DataAccessAdapter class and use the active adapter instance in the overridden methods (the Me instance) to save the audit entity (so that the audit entity is included in the same Save/Delete Transaction of the active instance of the adapter) ??

Walaa avatar
Walaa
Support Team
Posts: 14951
Joined: 21-Aug-2005
# Posted on: 05-Dec-2005 15:18:46   

I think this is possible, Just check in the OnSaveEntity() that the entity you are originally saving is not the AuditEntity, otherwise you will have an endless loop which will give a stack overflow exception.

NickD
User
Posts: 224
Joined: 31-Jan-2005
# Posted on: 16-Dec-2005 21:31:05   

I want to exactly what Omar is trying to do, but I can't get the code right, apparently. Here is my overloaded adapter code:

public class NCSDataAccessAdapter : DataAccessAdapter
    {
        string _userName;

        public NCSDataAccessAdapter():base()
        {
            
        }

        /// <summary>
        /// A constructor to set the username value to be persisted as current_user
        /// </summary>
        /// <param name="userName">The user name value to mimic current_user</param>
        public NCSDataAccessAdapter(string userName):base()
        {
            _userName = userName;
        }

        /// <summary>
        /// A constructor to set the username value and to set the connection string
        /// </summary>
        /// <param name="connectionString">The connection string to pass to the overloaded class</param>
        /// <param name="userName">The username to persist as current_user</param>
        public NCSDataAccessAdapter(string connectionString, string userName):base(connectionString)
        {
            _userName = userName;
        }

        /// <summary>
        /// Overloaded Method that will first set the userName value mimicing current_user
        /// </summary>
        /// <param name="entityToSave">The entity to be saved</param>
        /// <param name="isInsert">If true then this entity is in the insertqueue, otherwise updatequeue</param>
        public override void OnBeforeEntitySave(IEntity2 entityToSave, bool isInsert)
        {
            
            for (int i = entityToSave.Fields.Count-1; i >= 0; i--)
            {
                EntityField2 ef = entityToSave.Fields[i] as EntityField2;
                if (ef.Name.ToLower() == "update_date")
                {
                    ef.CurrentValue = this._userName;
                    break;
                }
            }
        }
       }

...and here is my test method (TestDriven.Net)

public void NCSDataAccessAdapterConstructTest()
        {
            NCSDataAccessAdapter da = new NCSDataAccessAdapter("testUser");
            string returnedUser;

            try
            {
                da.StartTransaction(IsolationLevel.ReadCommitted,"userTest");

                HlpUserEntity hu = new HlpUserEntity(4);
                da.FetchEntity(hu);

                hu.City="Changed";

                da.SaveEntity(hu,true);

                da.Commit();

                returnedUser = hu.UpdateUser;
            }
            catch
            {
                da.Rollback();
                throw;
            }
            finally
            {
                da.Dispose();
            }

            Assert.AreEqual(returnedUser,"testUser");
        }

but my Assert always comes back false. I put a breakpoint in my overloaded OnBeforeEntitySave method in the code above and it never gets there. What am I missing?

I can't even tell if my code inside of the overloaded OnBeforeEntitySave method is going to work because I can't seem to even get there.

P.S. I have removed the trigger on the back end.

NickD
User
Posts: 224
Joined: 31-Jan-2005
# Posted on: 20-Dec-2005 16:47:51   

I'm still not clear on what I'm doing wrong here. Anyone got any clues? disappointed

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39618
Joined: 17-Aug-2003
# Posted on: 20-Dec-2005 17:50:11   

The entity IS saved? The change, I mean. Or is there no query generated? Because SaveEntity will always add the entity to a queue and process that and in the persistqueue routine (the processor) always OnBeforeEntitySave is called, without exceptions...

Frans Bouma | Lead developer LLBLGen Pro
NickD
User
Posts: 224
Joined: 31-Jan-2005
# Posted on: 20-Dec-2005 18:03:30   

Yes, the entity is saved with the change to the city column, but I never hit the breakpoint in the OnBeforeEntitySave method inside my NCSDataAccessAdapter.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39618
Joined: 17-Aug-2003
# Posted on: 20-Dec-2005 18:58:43   

NickD wrote:

Yes, the entity is saved with the change to the city column, but I never hit the breakpoint in the OnBeforeEntitySave method inside my NCSDataAccessAdapter.

Very strange. As it IS saved, it IS passing the call to OnBeforeEntitySave... Did you by any change run a release build (I know, silly question, but then vs.net won't hit any breakpoints but won't tell you why)

Frans Bouma | Lead developer LLBLGen Pro
NickD
User
Posts: 224
Joined: 31-Jan-2005
# Posted on: 21-Dec-2005 00:45:46   

OK - after a lot of just staring at my code, I figured out that it is doing exactly what I told it to do, which unfortunately, is not what I WANT it to do.

So, your code works just fine, probably like you suspected all along. Part of the problem was that in my test code, I hard coded the value to set the city column to, and since your code is so smart, it would not actually update the entity because nothing had been changed in it after the first time I changed that record to "Changed" value. flushed

BUT, I now need your help in figuring out how to actually set the value of the column I'm after. Here's my best guess of what should go in my overloaded "OnBeforeEntitySave".

for (int i = entitySaved.Fields.Count - 1; i >= 0; i--)
            {
                EntityField2 ef = entitySaved.Fields[i] as EntityField2;
                if (ef.Name.ToLower() == "update_user")
                {
                    ef.CurrentValue = this._userName;
                    break;
                }
            }

...I want to start at the "bottom" of the entity because we put our credential tracking rows at the bottom of the table. Am I discovering the column name correctly here? What's my best course of action for setting just the one (or two as the case may be -- I also have update_date) columns?

Walaa avatar
Walaa
Support Team
Posts: 14951
Joined: 21-Aug-2005
# Posted on: 21-Dec-2005 09:08:30   

You may directly call EntityToSave.SetNewFieldValue(), which is better than looping.

NickD
User
Posts: 224
Joined: 31-Jan-2005
# Posted on: 21-Dec-2005 16:19:55   

Which works, as long as that column name exists. How can I verify that the column exists before I try to set it, which cause an "Object not set to an instance of an object" exception?

As best I could tell, there wasn't a FindField method in any of the interfaces.

NickD
User
Posts: 224
Joined: 31-Jan-2005
# Posted on: 21-Dec-2005 16:33:59   

I also noticed in the process of figuring this functionality out, that my "insert_date, insert_user, update_date, and update_user" columns have all been pre-defined as ReadOnly. Does LLBLGen know somehow that these are my credential columns? Can I turn this behavior off?