Fetching an Entity which is being Audited.

Posts   
 
    
Posts: 112
Joined: 09-Aug-2004
# Posted on: 19-Dec-2008 01:36:30   

LLBLGen v2.6 for .NET 3.0 & SQL Server 2005. Adapter template.

I have got an existing asp.net application I am layering auditing into. On our edit pages we are reconstructing the entities from scratch, passing the Primary Key, and setting IsNew to false (In other words, not fetching on postback, just calling Save). When we save the entity we'd like to record the old value in the audit table.

I was looking for an easy approach to integrate auditing and I thought fetching the entity in the Auditor and comparing the fields would be the best solution because it should work across the board for all entities.

When I actually try to do the fetch I am getting a timeout exception from the adapter. I am guessing the reason is because the entity is in a transaction with a lock on it. I know it is considered a bad practice to do selects while in a transaction, but is there anyway I can make this particular cause work as I described? It would be fairly difficult to go back and change our existing edit pages to fetch the entity before changing the fields.

Here is some code to illustrate the exception


    //IEntityCore entity has not been fetched yet!
    public override void AuditUpdateOfExistingEntity(IEntityCore entity) {
        IEntity2 old = null;
        IEntityFieldCore id = entity.GetFieldByName("Id"); //brittle I know
        if (id != null) {
            using (DataAccessAdapter adapter = new DataAccessAdapter()) {
                old = GeneralEntityFactory.Create((EntityType)entity.LLBLGenProEntityTypeValue);
                old.SetNewFieldValue("Id", id.CurrentValue);
                adapter.FetchEntity(old); //error: Timeout exception
            }
        }

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 19-Dec-2008 04:54:15   

I'm thinking about a workaround using expressions and avoiding do the fetch. Where are you aduting at (database - the same table, database - another audit table, another audit target)?

David Elizondo | LLBLGen Support Team
Posts: 112
Joined: 09-Aug-2004
# Posted on: 19-Dec-2008 14:49:43   

Hmm, That sounds interesting but I am not seeing how it would work.

There is an AuditInfo table which stores the EntityName, EntityId (Common throughout all the tables there is an "Id" field), OldValue, ModifiedDate and Comments. This is in the same database as all the other tables.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 19-Dec-2008 19:47:25   

Mmm.. That's a problem indeed. You somehow must send the old id to the audit action. If you are trying to fetch on Audit, then Why not fetch the entityToSave earlier?

David Elizondo | LLBLGen Support Team
Posts: 112
Joined: 09-Aug-2004
# Posted on: 19-Dec-2008 19:51:40   

There is no problem sending the old id (the id doesn't actually change). Infact we have to have the old id in order to save the entity.

The problem is we cannot fetch the old values using the id because the record is locked in the transaction because of the update (my assesment).

Here is some more code again, I think can speak in C# better than english, and that is sad because I only speak english.



int customerId = int.Parse("231");

CustomerEntity cust = new CustomerEntity(customerId); //Auditor inserted via DI
cust.IsNew = false;
cust.Name = "New Name";
cust.Email = "email";

adapter.SaveEntity(cust); //timeout exception

--------------------------------------------------------------------------------------

    public override void AuditUpdateOfExistingEntity(IEntityCore entity) {
        IEntity2 old = null;
        IEntityFieldCore id = entity.GetFieldByName("Id"); //brittle I know
        if (id != null) {
            using (DataAccessAdapter adapter = new DataAccessAdapter()) {
                old = GeneralEntityFactory.Create((EntityType)entity.LLBLGenProEntityTypeValue);
                old.SetNewFieldValue("Id", id.CurrentValue);
                adapter.FetchEntity(old); //error: Timeout exception
            }
        }


Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 22-Dec-2008 11:26:36   

You need to fetch the entity anyway, IMHO that's best done outside of the Auditor.

int customerId = int.Parse("231");
CustomerEntity cust = new CustomerEntity(customerId);

using (DataAccessAdapter adapter = new DataAccessAdapter(true)) 
{
    adapter.FetchEntity(cust);

    cust.IsNew = false;
    cust.Name = "New Name";
    cust.Email = "email";

    adapter.SaveEntity(cust);
}
Posts: 112
Joined: 09-Aug-2004
# Posted on: 22-Dec-2008 14:27:00   

Our only problem with that approach is we have a ton of code all over the place using DTO's and webservices. Updating all of this code is a more difficult solution than fetching the auditor some how.

Is it at all possible to accomplish what I am trying to do? Or is fetching the entity outside of the auditor the only solution?

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 22-Dec-2008 15:29:10   

The idea is that you should read the database values before the record gets locked for an update.

So maybe you need to derive from the DataAccessAdapter and override SaveEntity() to accomplish this for you.

DvK
User
Posts: 318
Joined: 22-Mar-2006
# Posted on: 29-Jun-2009 17:16:35   

Hi,

I'm experiencing the same issue here....time-out errors on execution of an Adapter.FetchTypedList.

What I want to do is, retrieve a dynamic typedlist for only the changed fields of a specific entity. This is of course the most optimal way of getting the old values to compare the new one's with. But when I execute this FetchTypedList on a newly created DataAccessAdapter in the AuditUpdateOfExistingEntity method, I run into time-outs.

As said in earlier/above messages, we're not using Fetched entities to perform incremental (single-field) updates (IsNew = False), so we need a way to get the original values.

Is there any possibility to use/retrieve the Adapter that was used to save the audited entity with ? That would solve the problem....

Regards, Danny

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 30-Jun-2009 04:34:28   

Hi Danny,

As Walaa said before, this is no trivial. The way it should be used is: - fetch entity - modify values - save entity (the audit info is logged here)

Or, in you case, you could do: - create a modified entity without fetch - identify the modified fields and fetch the original values, then set those values to your entity's DBValue - save entity (the audit info is logged here)

P.S. in the future please start a new thread, instead of reopen an old one wink

David Elizondo | LLBLGen Support Team
DvK
User
Posts: 318
Joined: 22-Mar-2006
# Posted on: 30-Jun-2009 10:06:45   

OK, thanks....I'll start a new thread next time ! But I'm not quite convinced. Why is it not possible to let the Auditor use the same Adapter to fetch the original field values for changed fields, before committing the entity to the database ?

That option would greatly improve the Auditor functionality. Following your proposal, implies that I have to implement an extra custom procedure that runs before every SaveEntity method to identify changed fields and set the DbValue accordingly, while thats now been taken care of by an automatic Auditor framework.

But what's the use of setting the DbValues after the CurrentValues already have been set ? The AuditEntityFieldSet method gets called on set of the fieldvalue and there you can compare the new <-> original value, so that only works on fetched entities. Also, we finally know what to fetch (original values) after all new values already have been set by a user.

Would it be an option to add an extra overridable method called TransactionStarting (like TransactionCommitted) where we can implement the custom 'fetch original values' logic ?

Regards, Danny

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 30-Jun-2009 10:45:00   

I think you first have to explain why you want to fetch original values during a save procedure (which could lock the transaction into a deadlock if you don't use the same transaction / adapter!) and not simply fetch the entities in the first place, update them in memory and save them?

Just because you can make an entity object in memory look like a 'fetched' entity (while it's new), it doesn't mean you should use that as the recommended way to deal with data. I.o.w.: fetch the entities, modify them, save them. It's much easier to deal with the data that way: almost no code, everything works as expected and no surprises. I.o.w.: maintainable software.

You're now jumping through hoops to get the same system in place at the expense of spending time writing that extra code, which also have to be maintained.

Frans Bouma | Lead developer LLBLGen Pro
DvK
User
Posts: 318
Joined: 22-Mar-2006
# Posted on: 30-Jun-2009 11:29:06   

Hey Frans,

I think you first have to explain why you want to fetch original values during a save procedure (which could lock the transaction into a deadlock if you don't use the same transaction / adapter!) and not simply fetch the entities in the first place, update them in memory and save them?

That's why I would like to use the same Adapter (from e.g. the Adapter.SaveEntity) in the entity-attached Auditor to prevent this situation from happening.

Just because you can make an entity object in memory look like a 'fetched' entity (while it's new), it doesn't mean you should use that as the recommended way to deal with data. I.o.w.: fetch the entities, modify them, save them. It's much easier to deal with the data that way: almost no code, everything works as expected and no surprises. I.o.w.: maintainable software.

We're dealing with a Silverlight application here which uses DTO objects (initial fetch) to display data in the GUI. That data can be modified by a user and then only the modified fields are being tracked by the application and send back to the WCF-layer as a 'modified fields' object. There, a 'new' entity (IsNew=False) is created by setting its PK value and the modified field values, and the save is performed using either a SaveEntity of UpdateEntitiesDirectly in order to make the UPDATE process as efficient as possible as we're only updating changed fields. That's what LLBLGen is all about, efficiency...not ?

I understand what you're advising...but that would imply that the changed entity will have to be fetched in full one more time when the changes come back from the Silverlight GUI to the WCF-layer. Now, that all sounds a bit unnecessary to me.... confused

Please react/advise !

Regards, Danny

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 30-Jun-2009 11:37:41   

There, a 'new' entity (IsNew=False) is created by setting its PK value and the modified field values, and the save is performed using either a SaveEntity of UpdateEntitiesDirectly in order to make the UPDATE process as efficient as possible as we're only updating changed fields. That's what LLBLGen is all about, efficiency...not ?

I understand what you're advising...but that would imply that the changed entity will have to be fetched in full one more time when the changes come back from the Silverlight GUI to the WCF-layer

You want to fetch the old values anyway.

In the WCF layer, you can just fetch the entity with only the fields being modified, using Include/Exclude fields, so you'll avoid fetching the entity in full. Then modify the fileds and save it.

DvK
User
Posts: 318
Joined: 22-Mar-2006
# Posted on: 30-Jun-2009 11:55:35   

True, but that will imply that every (Adapter-based) Save-action that's being called by the WCF must be monitored while the Auditor supplies a centralized solution for this. That's kind of a pity.....

Regards, Danny

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 01-Jul-2009 10:09:38   

DvK wrote:

Just because you can make an entity object in memory look like a 'fetched' entity (while it's new), it doesn't mean you should use that as the recommended way to deal with data. I.o.w.: fetch the entities, modify them, save them. It's much easier to deal with the data that way: almost no code, everything works as expected and no surprises. I.o.w.: maintainable software.

We're dealing with a Silverlight application here which uses DTO objects (initial fetch) to display data in the GUI. That data can be modified by a user and then only the modified fields are being tracked by the application and send back to the WCF-layer as a 'modified fields' object. There, a 'new' entity (IsNew=False) is created by setting its PK value and the modified field values, and the save is performed using either a SaveEntity of UpdateEntitiesDirectly in order to make the UPDATE process as efficient as possible as we're only updating changed fields. That's what LLBLGen is all about, efficiency...not ?

Never pre-maturely optimize your code. It's not said that using entity fetches is less optimal, simply because from the looks of it, you have to fetch the data from the DB anyway, as you have to audit value changes.

So to me you'd better do: - fetch the entities which were changed based on PK values specified in the change set received from the client - update these entities with the values received from the client. This will mark fields which are changed to 'changed' and ignore field changes which aren't really field changes (e.g. another client altered the same field already!) - simply save the entities again.

Very simple code and it works always and auditing is transparently build in. Less efficient? You can optimize this with checking before the fetch if you have altered a non-pk field F in entity type E and if not, exclude that from fetching. So if the client altered 3 customer entities and the client altered field F1, F2 and F5, you fetch the 3 customer entities with fields F1, F2, F5 and the PK and you alter the fields on the 3 entities, save them, done. this is as efficient as you would be with your own code as well, but it at the same time uses way less code and is easier to maintain.

Furthermore, fetching these entities really doesn't count compared to the overhead and slowness of a WCF channel.

This also doesn't need cumbersome code de-centralized somewhere, it's simple entity fetch logic and auditors are plugged in automatically. So I don't really see why you need monitoring code as it's just straight forward entity fetching/updating/saving.

Frans Bouma | Lead developer LLBLGen Pro
Suresh
User
Posts: 6
Joined: 19-Feb-2010
# Posted on: 12-Apr-2010 15:46:19   

DvK wrote:

Hey Frans,

I think you first have to explain why you want to fetch original values during a save procedure (which could lock the transaction into a deadlock if you don't use the same transaction / adapter!) and not simply fetch the entities in the first place, update them in memory and save them?

That's why I would like to use the same Adapter (from e.g. the Adapter.SaveEntity) in the entity-attached Auditor to prevent this situation from happening.

Just because you can make an entity object in memory look like a 'fetched' entity (while it's new), it doesn't mean you should use that as the recommended way to deal with data. I.o.w.: fetch the entities, modify them, save them. It's much easier to deal with the data that way: almost no code, everything works as expected and no surprises. I.o.w.: maintainable software.

We're dealing with a Silverlight application here which uses DTO objects (initial fetch) to display data in the GUI. That data can be modified by a user and then only the modified fields are being tracked by the application and send back to the WCF-layer as a 'modified fields' object. There, a 'new' entity (IsNew=False) is created by setting its PK value and the modified field values, and the save is performed using either a SaveEntity of UpdateEntitiesDirectly in order to make the UPDATE process as efficient as possible as we're only updating changed fields. That's what LLBLGen is all about, efficiency...not ?

I understand what you're advising...but that would imply that the changed entity will have to be fetched in full one more time when the changes come back from the Silverlight GUI to the WCF-layer. Now, that all sounds a bit unnecessary to me.... confused

Please react/advise !

Regards, Danny

Hi,

I am new to silverlight with LLBL & DTO. Please provide the sample application or small POC application for intergarating Silverlight project and LLBL DAta layer.

Please help , I was not able to follow any steps.

It would be helpful for to understand.

Thanks, Suresh.S

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 12-Apr-2010 15:59:46   

Please start your own thread. Forum guidelines here: http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=7725

Currently we don't have a Silverlight example. So you'll have to tell us what you have tried and what went wrong, so we can help you out.

Please make sure you include all the details in your new thread as mentioned in the forum Guidelines thread.

Thanks.