Newbie q: BusinessBase object Id is calculated via stored proc

Newbie q: BusinessBase object Id is calculated via stored proc

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


dbro199 posted on Sunday, September 07, 2008

I'm rewriting the user-interface for an existing system, and the database I'm working against doesn't have an autonnumber id field.  The field is calculaed based on a stored proc.

Two questions:
1) At what point should I assign the Id?  The Client object has a child collection  of addresses, which, if I add them, they'll need the Client_id filled in so they can use that as part of their key.  So should I grab a new client_id at create?  Will the framework handle this on it's own?  If not, how do I push the client_id to the client_id field in the child object.

2) How can I simply execute a stored proc (to get the NextKeyValue) ? I've got the csla2.x book sitting in front of me, and I can't find an example for the life of me.  I know I should be able to figure this out: the book and the few forum posts that talk about this allude to the how simple it is, but it's so simple, no one posts a full example.

By all means point me to a section of the book to re-read, but I can't find an example of executing a stored proc that returns a value with 2 hours of googling. :-(

Any help or pointers are greatly appreciated.

Don

triplea replied on Sunday, September 07, 2008

1) I guess there are 2 ways of doing this. The first is to assign the ID when creating your object. To do that you could execute a command object ( : CommandBase) that would return the new ID and you would assign it to your ID field. An example of a command object is the ExistsCommand within Project.cs (int the library project). The drawback of this is that you might be creating IDs you wont be using (i.e. if you dont save a new Client). The second option would be to assign the ID value when you are in the DataPortal_Insert of your client. There with a simple ADO call (or Linq or whatever you are using) assign the ID. Then when you call _addresses.Update(this) you will pass the prent with the now populated ID to the child in order to save the value. There are more ways of doing this but this should sufficeas a guide I guess.

2) Not sure what you mean. If you are looking for a logical object which would execute the stored proc for you then probably the command object as mentioned above should do. Not sure what chapter it is covered in, dont have the book in front of me. Now how you execute the stored proc itself is up to you. ADO.Net, Linq to SQL, something else... As long as its in a DataPortal_XYZ (or create without the RunLocal attribute) or a command object then you should be fine.

Hope this helps

dbro199 replied on Sunday, September 07, 2008

Hum. I guess I didn't explain myself well enough.

I have a client table with a 'client_id' column (it's unchangeable by the user). The number is a char(12) field, but it contains number values, based on a REF_KEY table, with the last used id for each table that needs these keys. So, there is a stored proc that returns the next avaliable number for a given table. Then, for the CLIENT table, there is another stored proc that calls the aforementioned one, pads the number out to 9 digits (why not 12, I'll never know), and then ensures there is no client_id with that number. if so, it goes back to the original stored proc to get another number, and re-checks this number, until an unused one is found. Okay. That's all there, and it's working.

Now, If I pull a number from that stored proc when I do a Client.NewClient() shared call, that's fine, I guess, except that if the user chooses not to actually save this client, I'll "waste" that Id. Is this a big deal? No, but it seems unnecessary to me somehow. So, I'd like to assign it at the same time as it would happen if I were using a normal autonumber integer indentifier field for the table: at the time the insert actually happens. Okay. But the Client has child collections (address, dependents, etc). So, with the save of the children happening automatically (by the CSLA framework) AFTER the root object's insertion, how can I propegate the parent's Client_Id value (returned from the Insert, I guess) down to the appropriate field on the child objects? I think I need a instance (not class) method on the child collection classes that can push the values down to the individual child items. Correct? And at the same time, because the child objects have a key of (Child_id, Seq_Num), I can assign an appropriate Seq_Num to any item that has a seq number of 0. I would call this collection method after inserting the root object, but before saving the child collections. Make sense?

If I take this approach then The retrevial of the next Client_Id value will happen within the InsertClient stored proc, so I technically don't need to understand this magical CommandBase object, but, to help augment my CSLA knowledge...

Next question of my original post.

The CommandBase object. Yeah. The book (CSLA 2.x) Expert VB2005 Business Objects talks about it, but in every case I can find, it has the code for the CommandBase class, without a single example of something that implements a CommandBase. Chapter 7 promises that there will be an example of this in Chapter 8 for the Exists method of, IIRC, the Project class. Yet, pouring over that chapter (admittedly, after a 20 hour work day), I can't find any hint of the example. And searching online has proven similarily unsuccessful. Every one says, "just use a CommandBase" object,but no one goes into detail.

I should be smart enough to figure this out, but I'm afraid I'm just not looking at it correctly. Suppose I have a Client object that inherits from BusinessBase. I want to have a "NextClientId" instance method that calls stored proc "GetNextClientId", passing in @NextClientId (in/out or out), and the table name, in this case "Client". I don't get the DataPortal stuff.

Should I declare a private class inside (inner class?) the Client object? If so, should it inherit CommandBase or simply ReadOnlyBusinessBase?

I'm not asking anyone to write my code for me, I'm simply hoping for someone to provide an example, with their code, that has done something similar. I'll adapt it to my needs. I have the stored proc, so that's fine. I just don't know how to hook that stored proc to CSLA object, considering that the stored proc doesn't return a result set. It simply updates the 'OUT' parameter. I suppose I could write a wrapper stored proc that ends with a "SELECT @NextClientId", forcing the creation of a result set and therefore is parallel to all our other CLSA CRUD procs. But it seems so unnecessary.

The previous thread talks about ADO.NET, Linq to SQL, etc, but that's not consistent with the rest of our CSLA approach. I know that CLSA objects can be written with Linq or ADO.NET calls, but I don't want to introduce another way of getting things done. Consistency is key to other developers understaning my code.

I can't speak my other companies, but at mine, hiding all the ORM stuff (yeah, I know, "Business Framework, not ORM framework" :Smile [:)] ) in CodeSmith generated classes, means that our developers don't need to know much about it. Simply copy things from another similar class. So now that I want to do "something different", I'm having to really understand how this works. In our environment, almost everyone other than the architects are simple developers with only a high level knowledge of the CSLA framework; they know enough to decide what type of CSLA object to inherit from, but that's about it. And my architect is unavaliable until he gets back from medical leave, so I'm kinda lost here.

I sincerly appreciate the help folks have tried to provide, and hope a bit more comes my way.

Thanks for reading this far.

--

Don Brown

triplea replied on Monday, September 08, 2008

Lets try 1 more time then :-)

dbro199:

Now, If I pull a number from that stored proc when I do a Client.NewClient() shared call, that's fine, I guess, except that if the user chooses not to actually save this client, I'll "waste" that Id. Is this a big deal? No, but it seems unnecessary to me somehow. So, I'd like to assign it at the same time as it would happen if I were using a normal autonumber integer indentifier field for the table: at the time the insert actually happens.

That's fine but just be careful how to handle transactions since if an insert fails during the execution of your dataportal insert then your IDs will not be in sync.

dbro199:

Okay. But the Client has child collections (address, dependents, etc). So, with the save of the children happening automatically (by the CSLA framework) AFTER the root object's insertion, how can I propegate the parent's Client_Id value (returned from the Insert, I guess) down to the appropriate field on the child objects?

Do you even need a field for the FK value on your child object? Its not really a requirement... The way you propagate the parent PK value is (in the PT example):

// update child objects
_resources.Update(this);

This call occurs after the parent insert so "this" (the project) has its PK Id field populated. This is how you propagate parent ID values to child objects.

dbro199:

If I take this approach then The retrevial of the next Client_Id value will happen within the InsertClient stored proc, so I technically don't need to understand this magical CommandBase object, but, to help augment my CSLA knowledge...

Yes that would be another valid approach. Just remember to populate your object with the new ID.

dbro199:

Chapter 7 promises that there will be an example of this in Chapter 8 for the Exists method of, IIRC, the Project class. Yet, pouring over that chapter (admittedly, after a 20 hour work day), I can't find any hint of the example.

Sorry again I dont have the book in front of me. But download and check out the PTracker example, it is in there (int Library\Project.cs). If you follow the SP route of populating the PKs then you wont need the CommandBase for your scenario but its a good thing to know.

dbro199:

I just don't know how to hook that stored proc to CSLA object, considering that the stored proc doesn't return a result set. It simply updates the 'OUT' parameter. I suppose I could write a wrapper stored proc that ends with a "SELECT @NextClientId", forcing the creation of a result set and therefore is parallel to all our other CLSA CRUD procs. But it seems so unnecessary.

The previous thread talks about ADO.NET, Linq to SQL, etc, but that's not consistent with the rest of our CSLA approach. I know that CLSA objects can be written with Linq or ADO.NET calls, but I don't want to introduce another way of getting things done. Consistency is key to other developers understaning my code.

The OUT paramter will be fine. No need to use wrapper SPs or anything complicated. The way you execute the SP is up to you. As long as it is in a DataPortal_XYZ method its up to you how you do it. CSLA is independent of the technology you use to access your data store.

Phew, hope this helps!

rsbaker0 replied on Sunday, September 07, 2008

If the id is numeric and not changeable by the user (e.g. typical autonumber behavior), then one technique is to use a "pseudo" id while the object is new and defer generation of the actual id until the object is actually saved.

The ORM I am using uses negative numbers for new object id's bound to autonumber database fields. Each type maintains it's own last used value which decrements each time one is required. When the object is saved, the negative number is replaced with the actual value generated by the database.

Copyright (c) Marimer LLC