UnitOfWork2 Feature Request

Posts   
1  /  2
 
    
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 21-Feb-2005 18:25:03   

Frans,

I make extensive use of UnitOfWork in a large application which has the following layers:

  • SOA Service Interface
  • Process Layer
  • Activity Layer
  • Entity Layer

Essentially the UnitOfWork is created in the Prcoess Layer and is passed to the Activity Layer which performs business activity work. The activity layer can call other activities within this layer and UnitOfWork collects changes to be made to the database which are committed in the Process Layer. This gives excellent performance as the Process Layer transaction is started at the last possible moment and its duration is kept to an absolute miminum.

The Service Interface facilitates a retry which caters for concurrency failures. If retries continue to fail, the last retry instructs the Service Layer to upgrade the transaction isolation level.

This all works GREAT except... I now want to include DeleteEntitiesDirectly, Action stored procedures and UPDATE... SET... WHERE... into the UnitOfWork.

Are you planning the inclusion of any of the above and if not, how much work to you think it would take for me to re-work UnitOfWork to include these requirements?

Marcus

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 21-Feb-2005 20:36:39   

I've to add some more methods to the Uow which collects the parameters and executes them at a later time. Proc calls are static method calls, which can be a problem, the other ones shouldn't be that hard for me to add, just some refactoring of the UoW element class and some more tracking collections. I'll add the DeleteEntitiesDirectly, UpdateEntitiesDirectly method support to the ToDo, and I'll see if I can cram it in before march simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 22-Feb-2005 10:44:37   

That's great news smile

Regarding the static proc call methods... Delegates to the rescue... wink

The templates would need to create a delegate for each main static ActionProcedure method call... this is easy enough and the AddForActionProcedure method would take the delegate as a parameter...

Alternatively... you could add an AddForCallback(AsyncCallback callback, object state) method. This would simply add a callback to the UOW execution queue and the action proc calls could be made within our own callback methods. This falls down in a distributed environment where the UOW is passed between tiers.

I do like the AddForCallback since it allows UOW to be extended so that code can be injected into the execution queue.

Maybe you can think of good reason why these ideas may be work... but I don't see any immediate problems... simple_smile

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 23-Feb-2005 13:01:53   

I don't know if you can create a delegate instance to a static method. I can imagine it won't work, I've to test that. simple_smile .

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 23-Feb-2005 16:48:03   

Otis wrote:

I don't know if you can create a delegate instance to a static method. I can imagine it won't work, I've to test that. simple_smile .

Yup Delegates work with static methods simple_smile

"A delegate declaration defines a reference type that can be used to encapsulate a method with a specific signature. A delegate instance encapsulates a static or an instance method."

From: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref/html/vcrefTheDelegateType.asp

Marcus

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 23-Feb-2005 18:53:43   

Excellent! I'll try to cram it in! simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 25-Feb-2005 10:06:51   

big problem: how am I going to call the delegates in generic code if they require parameters? I have to test it, but I can't just pass an array.

I can solve the variable parameter specification when adding the delegate, using params ... . But I can't call the delegate, at least I don't see how to do that.

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 25-Feb-2005 13:03:01   

Otis wrote:

big problem: how am I going to call the delegates in generic code if they require parameters? I have to test it, but I can't just pass an array.

I can solve the variable parameter specification when adding the delegate, using params ... . But I can't call the delegate, at least I don't see how to do that.

I'm not exactly sure what you mean by generic code simple_smile , but maybe I was thinking about it from a slightly different angle.

Regarding the adding of action stored procedures to the UnitOfWork, lets call the add method "AddActionProcedure"...

Say we have a generated action stored procedure called MyActionProc which takes 2 parameters like:

ActionProcedures.MyActionProc (string param1, string param2);

A new template is required to generate the delegates for each of the ActionProcedure methods. For the above example it would be called "MyActionProcDelegate" which has the same signature as MyActionProc. These are generated into a file called "ActionProcedureDelegates.cs".

delegate MyActionProcDelegate(string param1, string param2);

UnitOfWork.AddActionProcedure then takes an instance of delegate as a parameter. Note the param type Delegate with a capital "D" which refers the to base class. Maybe you need "MulticastDelegate" here, I'm not sure.

public void AddActionProcedure(Delegate actionProcedureDelegate)
{
...
}

and is called like this:

UnitOfWork uow = new UnitOfWork();
uow.AddActionProcedure(new MyActionProcDelegate (ActionProcedures.MyActionProc (param1, param2));
uow.Commit();

Of course the new Template could provide further cleanup of the above usage by providing a helper method like:

uow.AddActionProcedure(ActionProcedureDelegates.MyActionProc(param1, param2));

Within the Commit method, you can now simply invoke the queued Delegate instance as normal.

I haven't tested any of this either simple_smile but it looks okay on screen! (I was about to say on paper). Maybe you'll find a flaw...

Marcus

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 25-Feb-2005 13:45:13   

The problem is invoking the delegate. I have to store the delegate somewhere with the parameters passed to the AddActionProcedure(). I can do that.

However I've to call the delegate from the UoW code, to get it actually executed. To my knowledge (but perhaps I miss something), you simply do: delegateInstance(param1, param2...);

And you create the delegate IMHO not as you describe it, but as: new MyActionProcDelegate (ActionProcedures.MyActionProc); , thus without the parameters.

But perhaps I only use one way of using delegates and is there another way, so I've to read up simple_smile

(edit): ah! delegate.DynamicInvoke() can do it! simple_smile You can pass an array of parameters, precisely what I wanted simple_smile . Looks awesome, will add it if this works ok simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 25-Feb-2005 14:02:38   

Otis wrote:

The problem is invoking the delegate. I have to store the delegate somewhere with the parameters passed to the AddActionProcedure(). I can do that.

However I've to call the delegate from the UoW code, to get it actually executed. To my knowledge (but perhaps I miss something), you simply do: delegateInstance(param1, param2...); And you create the delegate IMHO not as you describe it, but as: new MyActionProcDelegate (ActionProcedures.MyActionProc); , thus without the parameters.

But perhaps I only use one way of using delegates and is there another way, so I've to read up simple_smile

Yes now that you say it, you're absolutely right! wink ... this was off the top of my head! And now I see what you mean...

Given this, maybe using a delegate is a red herring here actually. All you need is an enum which defines the ActionProcedure and the "params object" to cope with the variable parameters as you described.

So it becomes:


uow.AddActionProcedure(ActionProcedureEnum.MyActionProc, param1, param 2)

Internally you switch on the enum and call a new generated overloaded ActionProcedures.MyActionProc() which takes the params object. Since this method knows what parameters it requires, the mapping can easily be done.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 25-Feb-2005 14:54:59   

That's also a possibility, however that will too give problems, as the UoW doesn't know what to call, where is the switch located? simple_smile I then have to introduce some kind of object, which implements an interface which is then plugged into the UoW instance...

However DynamicInvoke will solve it. I generate for each proc call a delegate, a developer can pass in a new instance of that delegate + the parameters to the method of the UoW. I then store the delegate + the params object internally and I add the adapter or the transaction to the method call parameters when I call DynamicInvoke (I create a new object[] array in which I copy the parameters and at the end I add the adapter or the transaction object). DynamicInvoke uses reflection, so the first call is a bit slow(er).

Although it might be a bit faster, I don't really find the large switch an elegant solution, as the switch can become really huge (say you have 500 procs or more?) and it is limited to procs.

With the delegate solution, as you said, you can effectively use it for your own methods as well, which have to be called during a UnitOfWork commit action, for example logging something or whatever. simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 25-Feb-2005 15:28:54   

Sounds like you've got it sorted... well done! simple_smile

Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 27-May-2005 13:19:27   

Frans,

As you know I'm using my modified ORMSupportClasses project to compile the support classes DLL...

I'm not sure if the source I'm using is up to date or not but the UnitOfWork2 doesn't seem to contain the mew AddUpdateEntitiesDirectlyCall or AddDeleteEntitiesDirectlyCall methods...

I then downloaded the latest source from the web site (20-may-2005) and see that UnitOfWork2 now contains "AddDeleteEntitiesDirectlyCall" but no "AddUpdateEntitiesDirectlyCall"...

Has this not been implemented yet or is the published source out of date?

Marcus

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 27-May-2005 13:40:32   

These were added to 1.0.2004.2. I'm not sure if you're using the 1.0.2004.1 sourcecode..

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 27-May-2005 14:01:14   

Otis wrote:

These were added to 1.0.2004.2. I'm not sure if you're using the 1.0.2004.1 sourcecode..

No, I downloaded the source today... "AddUpdateEntitiesDirectlyCall" appears to be missing from UnitOfWork2

// You can specify all the values or you can default the Revision and Build Numbers 
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.2004.2")]

// Assembly file version. Use this version to signal file differences after a hotfix.
[assembly: AssemblyFileVersion("1.0.20042.050520")]

downloaded from http://llblgen.com/pages/secure/runtimelibraries.aspx

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 27-May-2005 14:44:21   

confused you're right..

It appears the method is accidentily named 'AddDeleteEntitiesDirectlyCall'.

I've uploaded a new batch of files just 1 minute ago.. sigh. Wished I had understood your previous message better. Thanks for reporting simple_smile I'll re-upload a new batch a.s.a.p.!

(thank <your higher mighty here> for finalbuilder simple_smile )

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 27-May-2005 14:48:19   

Otis wrote:

confused you're right.. It appears the method is accidentily named 'AddDeleteEntitiesDirectlyCall'. I've uploaded a new batch of files just 1 minute ago.. sigh. Wished I had understood your previous message better. Thanks for reporting simple_smile I'll re-upload a new batch a.s.a.p.!

In Ireland we can this "Murphy's Law"... Buttered bread always lands on the floor butter side down etc... No rush, I don't need it today. Cheers.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 27-May-2005 14:56:36   

Marcus wrote:

Otis wrote:

confused you're right.. It appears the method is accidentily named 'AddDeleteEntitiesDirectlyCall'. I've uploaded a new batch of files just 1 minute ago.. sigh. Wished I had understood your previous message better. Thanks for reporting simple_smile I'll re-upload a new batch a.s.a.p.!

In Ireland we can this "Murphy's Law"... Buttered bread always lands on the floor butter side down etc...

Heh, we call that here 'law on preservation of misery' wink (if I translated that right). But murphy's fine work is also well known here wink

No rush, I don't need it today. Cheers.

Heh, well I like new stuff to be accurate wink .

You can grab the new runtime libs now. Also be sure to grab the new templates as well. They contain some bugfixes in SetNewFieldValue.

The full installer is up in 22 seconds now. simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 27-May-2005 18:08:33   

Otis wrote:

The full installer is up in 22 seconds now. simple_smile

Great. Thanks.

Looks like I'm going to be the first to try these methods out! Will let you know if there are any issues.

Have a good weekend (can you believe its the weekend again so soon!!! Where does the time go frowning )

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 27-May-2005 19:00:41   

Marcus wrote:

Otis wrote:

The full installer is up in 22 seconds now. simple_smile

Great. Thanks.

Looks like I'm going to be the first to try these methods out! Will let you know if there are any issues.

I did write some unittests when I wrote them, though it's a bit hard to keep unittesting these methods, so these unittests weren't kept. Why the method was renamed accidently is beyond me. Let me know if you run into probs with them simple_smile

Have a good weekend (can you believe its the weekend again so soon!!! Where does the time go frowning )

heh simple_smile thanks you too! simple_smile . It's 31 degrees Celcius now here, so all you can do is lay in the sun on the beach wink

Frans Bouma | Lead developer LLBLGen Pro
JimFoye avatar
JimFoye
User
Posts: 656
Joined: 22-Jun-2004
# Posted on: 27-May-2005 20:05:11   

Here in America we have formalized Murphy's Law as "Anything that can go wrong will".

I often joke with my clients about Jack Murphy following me or lurking about the office.

Don't know if there was really a Murphy and if his first name was Jack.

Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 31-May-2005 13:36:53   

Otis wrote:

Let me know if you run into probs with them simple_smile

It's hard to determine whether this is a problem or not... It think it is simple_smile

AddDeleteEntitiesDirectlyCall is processed AFTER AddForDelete in UnitOfWork2 which is causing an issue. I expected deletes to be actioned in the order they were added to uow.

Here's the issue. Let's say you have an OrderEntity which has a 1:m relation with OrderItemEntities. If you want to delete in the most efficient way, you (due to referential integrity rules):

Delete the OrderItems using AddDeleteEntitiesDirectlyCall Delete the Order using AddForDelete

but UOW2 calls AddForDelete first, and hence the transaction aborts because of the integrity violation.

I'm not sure this is as simple as just calling AddForDelete first and then AddDeleteEntitiesDirectlyCall second because I'm sure there are instances where you might want deletes to happen exactly in the order the are specified.

As a workaround I have removed the AddForDelete calls and replaced them all with AddDeleteEntitiesDirectlyCalls for now... disappointed

But I thought that given I'm the first to try out these new methods in practice, it might not be too late to make changes to the implementation.

(I guess the same applies to AddUpdateEntitiesDirectlyCall if the updates concern updating the PK)

M

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 31-May-2005 20:34:12   

I first had a routine in place which detected the order in which the entities could be deleted. Then I realized: this is nonsense, as there can be an entity in the db, not fetched, which will cause the FK to violate. I.o.w.: without fetching all entities, it's undoable to detect the order, with solely the objects in memory.

With a delete directly, it's unknown which entities are matching the query. Therefore it's up to the developer what will happen, I can't make that decision, i.e.: no matter what order I'll execute the 2 different calls, there will always be a situation in which it won't work.

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 31-May-2005 22:41:00   

Otis wrote:

With a delete directly, it's unknown which entities are matching the query. Therefore it's up to the developer what will happen, I can't make that decision, i.e.: no matter what order I'll execute the 2 different calls, there will always be a situation in which it won't work.

Which is why I thought they should execute in the order they were added to UOW... this means that they have to be added to a combined EntitiesToDelete collection in UOW and switch on their type in order to call the correct concrete delete method.

This works even when the delete is recursive as the dev has untimate control over that too. This way there is never a situation that "will" fail"...

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 01-Jun-2005 10:37:45   

Marcus wrote:

Otis wrote:

With a delete directly, it's unknown which entities are matching the query. Therefore it's up to the developer what will happen, I can't make that decision, i.e.: no matter what order I'll execute the 2 different calls, there will always be a situation in which it won't work.

Which is why I thought they should execute in the order they were added to UOW... this means that they have to be added to a combined EntitiesToDelete collection in UOW and switch on their type in order to call the correct concrete delete method.

This works even when the delete is recursive as the dev has untimate control over that too. This way there is never a situation that "will" fail"...

At the moment the order in which the different calls are added to the UoW isn't tracked. I'll add it to the todo list, not sure when this feature will be implemented though, as the current update in development is the last one for 1.0.

Frans Bouma | Lead developer LLBLGen Pro
1  /  2