Foreign Key Properties

Foreign Key Properties

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


d0tnetdude posted on Thursday, January 25, 2007

Just curious to see how everyone else is handling this.  We have a Project and it is associated to a Customer.  The Project BO has a read/write property called CustomerID and a read property called CustomerName.  We need to display the friendly name of the Customer, but not allow the user to modify it.  Now when the user changes the Customer for the Project, we respond to the PropertyChanged event for CustomerID and update the value of CustomerName from the DB.  

While this is nice in a small class, we have some classes that have multiple foreign key references and I'm just concerned this may get a bit database intensive.

Any other thoughts or is this how everyone else is handling it?

Thanks! Matt

ajj3085 replied on Thursday, January 25, 2007

The only workaround I can think of which would avoid hitting the db would be to cache the list of customers using NVL subclasses.

david.wendelken replied on Thursday, January 25, 2007

d0tnetdude:

Just curious to see how everyone else is handling this.  We have a Project and it is associated to a Customer.  The Project BO has a read/write property called CustomerID and a read property called CustomerName.  We need to display the friendly name of the Customer, but not allow the user to modify it.  Now when the user changes the Customer for the Project, we respond to the PropertyChanged event for CustomerID and update the value of CustomerName from the DB.  

While this is nice in a small class, we have some classes that have multiple foreign key references and I'm just concerned this may get a bit database intensive.

I often create a set of GetPublicId functions for all my database tables.  Each function is responsible for returning a user-understandable text value that uniquely identifies the record to the user.

So, on a Person table, I would have a "PersonGetPublicId( PersonId int)" database function. It returns a string value like "Lhotka, Rockford P (Emp# 12345)".

That makes it easy for a programmer to get a useful, standardized string to pass back for error messages, brief descriptive labels, etc.  They are particularly useful for feeding NVList classes, particularly if your BO programmers don't understand the database as well as you would like.

The same concept could be used on the BO side of the code, except you would add a static function to the various business objects (and a property as well.) 

 

d0tnetdude replied on Thursday, January 25, 2007

That's kind of what I am doing now ... I have a helper class I call DBValue, that takes a Table Name, ValueColumnName, and DisplayColumnName (what is returned).  I have a Friend Shared Function on my Customer class called GetCustomerName(customerID).   This returns the String representation of the Customer Name and sets in the Project object.  I also need to start checking to make sure the CustomerID even exists, for now, I assume it does, but could I somehow roll that into this as well or would this be another database hit?  Or am I over reacting to the interaction with the database?

Thanks! Matt

ajj3085 replied on Friday, January 26, 2007

d0tnetdude:
That's kind of what I am doing now ... I have a helper class I call DBValue, that takes a Table Name, ValueColumnName, and DisplayColumnName (what is returned).  I have a Friend Shared Function on my Customer class called GetCustomerName(customerID).  


What layer is the client of that class?  I hope not the UI layer..

d0tnetdude:
This returns the String representation of the Customer Name and sets in the Project object.  I also need to start checking to make sure the CustomerID even exists, for now, I assume it does, but could I somehow roll that into this as well or would this be another database hit?  Or am I over reacting to the interaction with the database?


Well, If you're having the UI set the value of CustomerId from a NVL class which is a list of customers, I think you'll be ok.  At the end of the day, if a value is put in that doesn't exist, the insert should fail because you have a FK reference to the customer table.  Your UI should be forcing the user to choose from a known list, and you have much less to worry about.  Even if you allow the user to type the id directly (which you shouldn't) your FK should prevent the transaction from successfully completing.

d0tnetdude replied on Friday, January 26, 2007

Nope ... it's not in the UI, it's in a Friend helper class in the Business Layer! Smile [:)]

My thinking is that I don't want to assume the UI is passing me a valid CustomerID, and I want the business objects to handle the validation, as opposed to a SqlException when the foreign key relationship is not valid.  The old way we did it was to let the UI set the CustomerID and set the CustomerName, the latter was meaningless as the next time the Project object was loaded it would retrieve the CustomerName. 

I am just trying to make the Business Objects do more of the validating and helping with the assignment of foreign key related variables, like the Name of the Project or the Name of the resources assigned to the Project.  My thinking is that I can make the UI even easier to code, by having more of the funtionality at the Business Object Layer.   My Business Objects will get "fat", but I'm not sure if that is really a problem.  Just interested to see how everyone else is doing this.

SonOfPirate replied on Friday, January 26, 2007

This is prevelant in my applications.  The way I have implemented this is to use a similar type of lazy-load approach as Rocky did in ProjectTracker.  Here's an example:

public System.Guid Customer
{

    get
    {
        CanReadProperty();
        return _customer;
    }
    set
    {
        if (CanWriteProperty() && (!_customer.Equals(value)))
        {
            _customer = value;
            _customerName = System.String.Empty;
            PropertyHasChanged();
        }
    }
}
   
public System.String CustomerName
{
    get
    {
        CanReadProperty();
   
        if (System.String.IsNullOrEmpty(_customerName) && (_customer != System.Guid.Empty))
            _customerName = Customer.GetCustomer(_customer).Name;
   
        return _customerName;
    {
}

This accomplishes the same thing by retrieving the customer record from the db and setting the internal variable to the name, but the overhead is not incurred unless the value is actually needed.

Note that I invalidate the _customerName variable in the Customer property setter.  This ensures that the correct value is returned should the id property be changed.  No need to handle any events this way.

HTH

P.S. I should add that for my read-only classes that are used strictly to display the object in lists, I create a View in the db and return either both in the result set or just the name.  What is shown above would only be used in an editable (child) object.

 

pelinville replied on Friday, January 26, 2007

I do what SonOfPirate does with just a slight change. 
 
Public Readonly Property CustomerID as Guid
   Get
      return lvCustomerID
   End Get
End Property
 
Private lvCustomer as Customer
 
Public Sub SetCusomer(cust as Customer)
   If cust Is Nothing then
      lvCustomerID = SmartGuid.MinGuidValue 'This is a guid with all zeros
      lvCustomer = Nothing
   Else
      lvCustomerID = cust.CustomerID
      lvCustomer  = cust
   End If
End Sub
 
Public Readonly Property Customer() as Customer
   Get
      If lvCustomer  is nothing then
         lvCustomer  = Customer.GetCustomer(lvCustomerID)
      end if
      return lvCustomer
   End Get
End Property
 
Public Property CustomerName() as String
      Get
         If me.Customer isnot nothing then
            Return me.Customer.Name
         Else
            Return "" 'Or whatever fullfills your business logic
         End if
      End Get
End Property
 
 

Copyright (c) Marimer LLC