GetIdValue() and Composite Keys

GetIdValue() and Composite Keys

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


Howard posted on Tuesday, May 16, 2006

Hi

I'm implementing an EditableRootCollection/EditableChild portion of our object model and as the subject suggests am curious as to how to implement GetIdValue() for data that does not have a single uniquely identifying integer key (surrogate key?).

In this particular case the Root has 2 keys and the Child has 3 keys of which 2 are shared with the root.

Having not fully read your book although I've read chapters 1,2 and skimmed most of the rest of it, the only dependencies on this method to my knowledge are Equals(), GetHashCode() and ToString() in BusinessBase<T>.

I was thinking of implementing 2 public structures say RootKey and ChildKey and use these inplace of the integer type ID in the templates.

Will this work? Is this best practice CSLA?

Any help much appreciated.

Thanks in advance

Howard

LayeredDev replied on Tuesday, May 16, 2006

Hi Howard,

I would suggest revising the data model and making a single uniquely identifying integer the primary key for both the root and the child. If your requirements require you to have unique values in a set of columns, then create a unique constraint/index on those columns in the root or child table but keep the primary key a unique integer column in both the root and child data sets.

Thank you,

 

Cosmic Ovungal

Howard replied on Tuesday, May 16, 2006

Thanks for the response, I'll pass this on to the DBA.

However, if the DBA turns round and says no, is the approach I outlined still valid/workable?

LayeredDev replied on Tuesday, May 16, 2006

Hi Howard,

The approach you outlined may work but it will not be standard CSLA practice. It will also lead to maintenance nightmare later on down the road. Thats because you are using composite key as a primary key. In my experience, the primary key should always be a single column, usually a unique integer which is given the identity property in SQL Server. 

If you are planning for replication or inserting data from other table, then you would choose a RowGuid as your primary key which generates a primary key based on NIC card on your machine. 

Cosmic Ovungal

xal replied on Tuesday, May 16, 2006

Seriously... you can't change your data model just because of GetIdValue!! Mainly because there's nothing wrong in having a composite pk.

One thing you can do in this cases, (I realize this may be frowned upon) is join all this values and get it's hashcode as your own internal id:

Dim pk1 as Integer = 10
Dim pk2 as String = "hello!"

String.Concat(pk1.ToString(), pk2).GetHashCode()


Andrés

LayeredDev replied on Tuesday, May 16, 2006

Andres,

There's is nothing wrong in having composite PKs but it complicates matters a lot. Its best to avoid having composite PKs because you have to take all the columns that compose a PK and import it into another table as a FK.

Its best to keep matters simple by having a single column PK and implementing business requirements of uniqueness among columns by creating a unique index/constraint on such columns. This has been my practical experience.

But if a composite key is required, then your method would be the way to go.

Cosmic Ovungal

Howard replied on Tuesday, May 16, 2006

Cosmic - What kind of maintenance nightmare you are referring to? Surely everything is encapsulated within the structure itself? With Equals() implemented and perhaps an operator override for =, I don't really see how this makes life any harder other than having to code a handful of very simple structures and having more parameters to pass around? Also, the structures and parameters themselves would be inherently more readable rather than surrogate integers or guids thus making debugging easier. Am I missing your point?

Andres - having just read the help on Object.GetHashCode() I think your solution is the only option short of actually implementing a custom hashing algorithm and I for one don't want to go there :)

Although of course you would need to make some effort to ensure that clashes do not occur perhaps by introducing a delimiter character between the strings?

Rockford - What is your stance/advice on this?

 

 

ajj3085 replied on Tuesday, May 16, 2006

I'll have to agree with xal here.  There are valid reasons for composite keys and your datamodel shouldn't change due to your business object model.

Whats wrong with having this:

protected override GetIdValue() {
  return string.Format( "{0}:{1}", intId1.ToString(), intId2.ToString();
}

Seems like that would be sufficient.

Andy

Howard replied on Tuesday, May 16, 2006

Yeah, doh (slaps head). Quite right. Just got off messenger with designer and came to exactly the same answer (with delimiter character tho.).

Thx.

RockfordLhotka replied on Tuesday, May 16, 2006

Absolutely - just concatenate the parts of the key with a delimiter and you should be all set. That's what I do. Sometimes compound keys are the right answer, no doubts there.

Alternately, GetIdValue() doesn't necessarily need to reflect the database at all. You could keep a Guid value private to your object as its "id" value while in memory. This can get confusing, so I don't know that it is always the right answer, but in the case that you don't have any other unique identifier for your object this can be the right answer.

hurcane replied on Thursday, May 18, 2006

I've been forced to work with composite keys for several years now. Concatenating is required. The only question is how to concatenate.

Delimiters is a good idea, but you have make sure you choose a delimiter that won't be in the value. In our database, some of the keys can contain any printable character.

Another option is concatenating padded fields. This is what we do. We maintain "smart" data objects in our editable business objects. These smart data objects store the maximum length of their fields when they are retrieved from the data reader. This maximum length is used by the UI to prevent the user from typing too many characters, but it is also used to provide a padded value. This approach works as long as your composite key columns are all varchar or char. If you get into dates and integers in your key fields, it's a little less intuitive, but the padded values could be have formats like 20060518 for a date or 000001458 (9 characters) for an integer.

malloc1024 replied on Thursday, May 18, 2006

Natural keys are great in theory but are usually lousy in practice.  This is why I usually use surrogate keys now.  If the user is able to change any of the values of the key, you will need to store the old values of the key in the object so you can update the key in the db.  DBA’s with little to no programming experience are probably more likely to use natural keys.  However, you should not change your db structure to fit your code.  I have used concatenation and GUIDs in the past.  Both methods work; however they are a hassle when compared to the alternative.

ajj3085 replied on Friday, May 19, 2006

There's nothing wrong with natural keys.  The problem is usually the mistake of thinking that natural key X can never change.  Using an SSN for example would have this problem.  It may change if the person is recovering from ID theft, for example.

However, a model number isn't likely to be recycled by a company, and I would say model number could be a great natural key.

At any rate, we are discussing composite keys, not natural keys. Smile [:)]

Andy

LayeredDev replied on Friday, May 19, 2006

Can't a composite key be a natural key? And vice versa can't a natural key be a composite key? Just wondering. And is SSN a natural key. I would think the DNA fingerprint would be the only natural key for humans. Again, just wondering.

Cosmic Ovungal

malloc1024 replied on Friday, May 19, 2006

Ajj3085,

The reason why I brought up natural keys is because composite keys are usually made up of natural keys.  If you use surrogate keys instead of composite keys, you have no need for composite keys.  BTW, I did not say all natural keys are bad.

RanceDowner1234 replied on Friday, May 19, 2006

I would suggest using a GUID instead...

    Private _id As Guid = Guid.NewGuid
    Protected Overrides Function GetIdValue() As Object
        Return _id
    End Function

I ran into a problem when trying to databind a collection to an editable datagrid.  If you use database values as the key for the object it will give you all kinds of woes if you allow the user to create new rows, if the Key values are not filled out until after a database update (for example using SQL Server 'seeded' fields).  The datagrid&CLSA apparently try to remove all rows that share the same value returned from GetIdValue( ) when an undo is called.  So unless you are generating any Key values automatically on Object creation, they will be blank for any newly created objects, until you save and return primary key from database.  Using a GUID seemed to solve all these problems.  The other workaround, as stated, would be to make sure that your values are autogenerated when you create the object.  But GUIDs just seem to be the most elegant solution.

Copyright (c) Marimer LLC