Simple question

Simple question

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


ajj3085 posted on Tuesday, October 17, 2006

Hi,

I have a phone number class.  It has a type, such as fax, cell, etc.  The types are loaded from the database so that we can extend them easily.

Now I have an object which has PhoneNumber and FaxNumber as properties.  Both of these properties are of type PhoneNumber.  Obviously I want to set the type and not allow changes.

Easy enough..

FaxNumber = PhoneNumber.New( PhoneTypeList.List.Key( "Fax" ) );

Not too crazy about hard coding the string Fax, but not too sure if there's a way around it (although i'll probably drop it in the resx file, but its still basically hardcoding Fax there).  Now, this table is not user modifiable, so maybe I'm worrying about nothing?

Andy

Henrik replied on Wednesday, October 18, 2006

Andy

 

I too have struggled to come up with a solution for this. Here’s how I solved it.

 

I have built a class library for Party/Address management which is being used in  several of my systems. The library is built on the Party/Role idea´s of Len Silverston, where all methods of contact to a Party is stored in a ContactMethod collection. Addresses are stored in an Address collection:

 

Party

  AddressList

        Address

  ContactMethodList

        ContactMethod

 

Each ContactMethod has a ContactMethodType.

 

In my first iteration I thought that I could just create an enum to represent the types of contact methods but soon found that another of my apps needed other types and some of the existing types had to be removed. So I put the types into a DB table and changed the code to be a NameValueList.

 

Now this posed a problem, since my code couldn’t just say: “Give me a Party’s Home Phone number” anymore, by specifying an enum id. It had to lookup the id of the “Home Phone” type in the NameValueList, by specifying the textual representation.

This posed another problem, since almost all of my apps are globalized/localized, where a copy of my app running in an english speaking country would have a “Home Phone” textual representation but a copy running in a danish speaking country would have a “Telefon hjem” textual representation.

 

First I thought of adding the localized textual representations to a resource file, but this defeated the purpose of having a dynamically changing ContactMethodType collection, since it would require me to add/remove textual representations in the resource files as well. Furhermore I had users running a danish copy of a system but with english localization. That totally broke this idea.

 

So I took the 1000 mile high view on what I was trying to solve and what I actually needed to do within my code. I found that there was only a handfull of ContactMethodTypes and AddressTypes that my code needed to know internally like.

 

So I added an extra “TypeDescriptor” field to my ContactMethodType table which uniquely identifies the types I know my code will need access to.

The following is an excerpt of a danish copy of my ContactMethodType table with type descriptors:

 

TypeId          TypeName                          TypeDescriptor

1                    Telefon hjem                       HomePhone

2                    Telefon arbejde                   WorkPhone

3                    Fax                                      Fax

4                    Anden Fax                         

5                    E-post 1                              Email

6                    Anden telefon                   

7                    Anden fax

8                   

 

The types that doesn’t have a type descriptor cannot be directly looked up by code.

 

When I design a new app I first select which type descriptors the code needs to lookup. Then I assign these to the ContactMethodTypes like above.

Then in my ContactMethodType NameValueList, I have a method that allows me to get the TypeId, so I can write code that looks like yours:

 

Dim phone As ContactMethod

phone = ContactMethod.Create(ContactMethodTypeList.FromDescriptor(“HomePhone”))

 

This allows me to “hardcode” that TypeDescriptors and still have the TypeName localized. I can still bind the NameValueList to a combo, where the user picks the localized version of a type.

 

It also allows a sysadm to add new ContactMethodTypes to the table without adding/altering code. Of course, if a sysadm removes a TypeDescriptor that is used in code, the NameValueList will return nothing and the code needs to handle this exception. I usually log an exception, for example  “HomePhone TypeDescriptor not found” and notifies the user of the problem.

 

I too thought that this was a lot for almost nothing, but even the seemingly simplest problems can be quite hard to tackle when you throw in dynamically changing lookup lists and localization.

 

I guess I’ve been babbling enough, but I hope you can use some or all of this.

 

Cheers

/Henrik

ajj3085 replied on Wednesday, October 18, 2006

This solution sounds fine to me.  Since I don't have to globalize the code though, it sounds like I can use the name as the descriptor.  Might be a good idea to have a seperate FromDiscriptor method though and use that, in case I do need globalization later.

Thanks!
Andy

SonOfPirate replied on Wednesday, October 18, 2006

The issue is not a big deal if you are limited to accessing the information blindly in your code, i.e. leaving it to the UI to make the distinction.  This would be the case when you have a drop-down list display all of the types and a textbox or label display the number.  Then, you can simply use what the user selected as your key.  Since both come from the DB, you know they will always be valid and expand/contract as your DB does.

However, what you are describing is the need to hard-code a specific type into a property.  First, are you sure you want to do this?  Assuming you are, you are left with the fact that at some point, no matter what approach you take, you are going to be left hard-coding the "key".  But, the good news is that you don't necessarily need to worry about extensibility and what happens with new keys, etc. because you only care about the "Fax" phone type.

This is an issue though when you consider how the types are created and what if "Fax" is removed from the database then re-entered as "Facsimile"?  You've probably considered this, so I'll move on.

The bottom-line is that by locking down your property to one type, you are going to have to hard-code the value whether it is from an enumeration, resource file, string, whatever, you will have to make sure it stays in sync with the DB.  That is the challenge.

And, even if you passed this off to client code and had a PhoneNumbers property, it only makes it so the client code has to deal with knowing the key instead of your class.  Eventually someone needs to know what value to use.  Short of having a DDL or some other dynamically generated souce for the user to select from, you'll have to hard-code it somewhere.

BTW - I too use a "descriptor" - we call it a "key" - that is used to identify records in lookup tables in a common language (english) to allow for globalization and not interfere with code.  Everything has a Key and a DisplayName with the latter in the applicable language for the user.

Hope that helps.

SonOfPirate replied on Wednesday, October 18, 2006

Andy,

You slipped your last post in while I was typing...

Your explanation was perfect to explain the use case for your problem.  I've run into this when developing (e)commerce apps where a customer is able to maintain several addresses but when an invoice is generated, the system must select the customer's current billing address and duplicate it into the Invoice (we duplicate and not reference so that the invoice will always reflect its state at the time it was generated and not be subject to future address changes).

In this case, we have to have code like:

Invoice.BillingAddress = Customer.Addresses["Billing"];

Which is exactly what you are doing.

Maybe an additional example will help.

 

ajj3085 replied on Wednesday, October 18, 2006

Pirate,

Yes that is what I am working on as well.  In this case our orders etc. have a fax and phone number (which I'm calling office..) and although it can be blank, the type is hard coded.

And like you, I will be keeping a snapshop of the data at the time the invoice was saved. 

I suspected this was the only way, but just wanted to see if a better solution had ever bee developed.

Thanks for taking the time to repond!

Andy

RedMark replied on Wednesday, October 18, 2006

Not sure if this helps but...

"The types are loaded from the database so that we can extend them easily" and

"Not too crazy about hard coding the string Fax"

are a bit contrary in that if you extend the types in the database you'll have to break into code and write some more hard-coded strings and coupled with the strongly-typed properties FaxNumber and PhoneNumber make this solution non-extensible

It's hard to know the answer without knowing what you are doing because some instances may be as simple as a foreign key in the db. but in others like, "Pick a primary comms. line?" Fax, Phone etc. etc. May be a different solution or is it a case of picking contact types and then entering numbers against them? Reading back that seems more likely...

First off I'd create a child collection of numbers from your objects and just use the Type Description (loaded from the database) as the key.

So just use the Type Code or Description as the identifier in code...

From a combo or something bind to the types list...

Numbers.Add (PhoneNumber.New( cboTypes.Value));

In the Add method check for the existance of PhoneNumber.Type in the collection.

 

Something of that ilk. Extendable and no hard-coding.

ajj3085 replied on Wednesday, October 18, 2006

RedMark:
"The types are loaded from the database so that we can extend them easily" and

"Not too crazy about hard coding the string Fax"

are a bit contrary in that if you extend the types in the database you'll have to break into code and write some more hard-coded strings and coupled with the strongly-typed properties FaxNumber and PhoneNumber make this solution non-extensible

I can see how you might see it as contrardictory.  There are two places the phone types are used.

One is when editing a contact.  The user has a grid allowing them to add phone numbers.   They pick the type and enter the number.  All types are always available.  If the users need a new type of phone number here, I can simply add it to the database (the user's do not maintain this table, I do).  This has already happened, I needed to add the type Lab.  Lab has no meaning whatsoever to the code, its just a flag.  The FQN of the class is Contacts.PhoneNumber.

The second area is sales / invoicing.  Here, there are two fixed fields allowing phone data; one marked Phone number and the other Fax.  The user can only use these two fields, and cannot change the type of phone number.  Whatever they entered into Fax is a fax number and will be flagged automatically as such.  This PhoneNumber (Quoting.PhoneNumber) class has a type property which is NOT exposed to the UI, the parent object asks that a PhoneNumber be created with a specified type, and once created, that type cannot change.   So this part of the application assumes that Office and Fax types always exist (and they will; users want these types of phone numbers, and I will not remove them).

RedMark:
So just use the Type Code or Description as the identifier in code...

From a combo or something bind to the types list...

Numbers.Add (PhoneNumber.New( cboTypes.Value));

In the Add method check for the existance of PhoneNumber.Type in the collection.

I think you can understand why this will not work in my second scenario.  Since the functionality of PhoneNumber differs in the two areas, I have two seperate classes to handle these.  The first just ensures that whatever type is entered is in PhoneTypeList.  The second requires that two types always exist.

It may be that hardcoding is acceptable in this case (and from the other poster, it sounds like it might be ok).  If I can avoid it, that'd be great and would love to hear other suggestions for doing so that still allow me to properly tag the type of phone number in the database (so that I can put them in the right property when loading the invoice).

Thanks
Andy

Copyright (c) Marimer LLC