asp tutorials, asp.net tutorials, sample code, and Microsoft news from 15Seconds
Data Access  |   Troubleshooting  |   Security  |   Performance  |   ADSI  |   Upload  |   Email  |   Control Building  |   Component Building  |   Forms  |   XML  |   Web Services  |   ASP.NET  |   .NET Features  |   .NET 2.0  |   App Development  |   App Architecture  |   IIS  |   Wireless
 
Pioneering Active Server
 Power Search








Active News
15 Seconds Weekly Newsletter
• Complete Coverage
• Site Updates
• Upcoming Features

More Free Newsletters
Reference
News
Articles
Archive
Writers
Code Samples
Components
Tools
FAQ
Feedback
Books
Links
DL Archives
Community
Messageboard
List Servers
Mailing List
WebHosts
Consultants
Tech Jobs
15 Seconds
Home
Site Map
Press
Legal
Privacy Policy
internet.commerce














internet.com
IT
Developer
Internet News
Small Business
Personal Technology
International

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

HardwareCentral
Compare products, prices, and stores at Hardware Central!

Building an End User Defined Data Model - Part 2
By Peter Scheffler
Rating: 4.0 out of 5
Rate this article


  • email this article to a colleague
  • suggest an article

    Part Two - Implementing the Data Model

    In the last article, we learned about the reasons for and against a normalized data model. We discussed he basic design of a normalized model versus a non-normalized model. We talked in abstract terms and in generalizations. In this article, we'll get into the meat of the model - we'll discuss real-world implementation specifics and the actual table layouts.

    Review

    In article one, we discussed the pros for using a user defined data model - we give your customer the power to manage their own attributes, beyond the application and without the need for costly development upgrades. For simple changes, this works well. We discussed an example where we needed a new attribute for a chair that wasn't in the initial design criteria. If we used a static data model, the implications are that you now need to change your data model which typically translates into an application change.

    Using the proposed model, these changes can be minimized significantly.

    The Data Model Specifics

    What we now can discuss is how, specifically, we can define our application and data model together such that we can accommodate these changes. In our previous article, we discussed things in abstract terms, here we will develop an specific implementation.

    Expanding on our chair theme from before, let's say we have a need to define a series of furniture for your customer, an office management company. Their need is to define a series of offices for their customer base, allowing for different downstream customer requirements and they want to allow their end users to see their own inventory.

    We would begin by defining a series of large 'objects' in your customers' world - here we have chairs, desks and plants in a simplified world.

    We define a standard table structure for each of these items:

    • Each has a master table

         Unique Identifier column
         Creation time stamp column
         Creator identification column
         Modification time stamp column
         Modifier identification column

    • Each has an attribute table

         Unique Attribute Identifier column
         Relation back to master table
         Attribute type identifier
         Attribute value
         Creation time stamp column
         Creator identification column
         Modification time stamp column
         Modifier identification column

    The tables themselves would look like this:

    The SQL would be:

    create table chair (
        chair_id bigint not null identity (1,1),
        created_on smalldatetime,
        created_by bigint,
        modified_on smalldatetime,
        modified_by bigint)
    
    alter table chair add 
        constraint pk_chair primary key  clustered 
        (
            chair_id
        ) 
    
    create table chair_attribute (
        chair_attribute_id bigint not null identity (1,1),
        chair_id bigint not null,
        system_attribute_id bigint not null,
        chair_attribute_value,
        created_on smalldatetime,
        created_by bigint,
        modified_on smalldatetime,
        modified_by bigint)
    
    alter table chair_attribute add
        constraint pk_chair_attribute primary key clustered
        (
            chair_attribute_id
        )
    

    We also need to define our meta-data table. I call this the system_attribute table. This table holds all of the attribute definitions for the whole database and also defines several criteria that are used to ensure data integrity.

    The system_attribute table is also broken into classes by the system_attribute_class table.

    These tables would look like this:

    The system_attribute_class table allows us to easily break up the various groups of attributes in the database - chairs versus plants in our example. This simply makes it easier to find the attributes in our table when we want to do a search.

    The system_attribute table is the meat of the whole system, however. This table is what drives our user interface, our data model and ultimately the application code.

    Looking at the column names, we have the following:

    SYSTEM_ATTRIBUTE_ID

    This is our identity column and is system generated. This will be the reference in our attribute tables (chair_attribute, desk_attribute, etc) to tell the application what the row refers to.

    SYSTEM_ATTRIBUTE_CLASS_ID

    This refers back to the system_attribute_class table, allowing us to segment the rows easily.

    SYSTEM_ATTRIBUTE_NAME

    This is the human readable name of our attribute. I typically use these as a look up into the table, since it is possible - although not suggested - that the id column may change in different implementations. Names like chair_color or desk_style would be examples of this column.

    SYSTEM_ATTRIBUTE_DESCRIPTION

    I use a description column in the table so that when you return to the application six months later you can easily see what it is you're looking at. When the system_attribute_name is chair_color it is pretty easy to remember what you were thinking at the time, but chair_nbr_leg might be a little confusing. I don't typically display this out to the application, but it could be used as a tool tip in some cases.

    SYSTEM_ATTRIBUTE_DISPLAY_TEXT

    This is the label for the user interface. Setting it here allows you to pull the text from the database nice and easily - you can localize easily and make UI changes without having to resort to changing the code.

    SYSTEM_ATTRIBUTE_DISPLAY_ORDER

    This is a sequential number which I use to drive the display of the items - zero to however many attributes you have for an item. When you return the results, sort by this and your UI will display in the order you want them to.

    SYSTEM_ATTRIBUTE_DEFAULT

    This allows us to define some default value for our item - we can leave it blank or we can populate it and if the application wishes, it can assume this default by passing a null value when the attribute is inserted.

    SYSTEM_ATTRIBUTE_VERIFY_SQL

    Use this to call a stored procedure to return a result back to your application telling it if the insert, edit or delete can take place. You can do bounds checking, verify foreign keys or other items. Putting it here allows you to drive the functionality into your application or into the stored procedure.

    SYSTEM_ATTRIBUTE_LOOKUP_SQL

    This is a powerful feature. Want to have the item be a dropdown combo? Set up a lookup stored procedure here and have the combo items populated directly. Further, we can build into our insert and update stored procedures the logic to verify that the attributes are within the list of this lookup.

    T-SQL to the Rescue

    Now we need an easy method of accessing our data. We'll do that by defining a standard set up stored procedures for us to use when accessing, deleting, editing or inserting records.

    We'll create stored procedures with the following naming convention:

    <table>_get
    <table>_insert
    <table>_update
    <table>_delete

    Therefore, we will get the following stored procedures:

    Chair_get; chair_insert; chair_update; chair_delete

    Chair_attribute_get; chair_attribute_insert; chair_attribute_update; chair_attribute_delete

    The SQL syntax for these would be:

    create procedure chair_get 
        @chair_id bigint
    as
        select * from chair where chair_id = @chair_id
    
    create procedure chair_insert
        @created_by bigint, 
        @chair_id bigint output
    as
        insert into chair (created_by, created_on) 
            values (@created_by, getdate())
    
        if @@ERROR <> 0
            begin
                -- not good, we need to reset the
                -- chair id and return the error
                @chair_id = 0
                return @@ERROR
            end
        else
            begin
                @chair_id = @@IDENTITY
                return 0
            end
    
    create procedure chair_update
        @chair_id bigint,
        @modified_by bigint
    as
        -- update the chair table, we only need
        -- to touch the modified columns since
        -- that's the only real data here
        update chair 
            set 
                modified_by = @modified_by, 
                modified_on = getdate()
                where chair_id = @chair_id
            if @@ERROR = 0
                return 0
            else
                return @@ERROR
    

    You can also modify this to handle an audit trail. As you'll see in the attribute functions, we call this whenever we make an attribute change and we could create a simple audit trail to track who makes the changes and when. For now, we can leave this as a simple 'last modified' tracker.

    create procedure chair_delete
        @chair_id bigint
    as
        -- delete the attributes before we move ahead
        delete chair_attribute where chair_id = @chair_id
    
        if @@ERROR <> 0
            return @@ERROR
    
        -- OK, we can proceed
        delete chair where chair_id = @chair_id
    
        return @@ERROR
    

    The attribute stored procedures are a little more complicated - they will need to refer back to the system_attribute table and to the main item tables.

    create procedure chair_attribute_get
        @chair_id bigint
        -- said is short for system_attribute_id
        @chair_said bigint = 0
    as
        -- get all the attributes for this chair
        if @chair_said = 0
            select 
                chair_id,
                chair.modified_on chair_modified_on,
                chair.modified_by chair_modified_by,
                chair.created_on chair_created_on,
                chair.created_by chair_created_by,
                chair_attribute_value,
                system_attribute_name,
                system_attribute_description,
                system_attribute_display_text,
                system_attribute_display_order,
                ca.modified_on chair_attribute_modified_on,
                ca.modified_by chair_attribute_modified_by,
                ca.created_on chair_attribute_created_on, 
                ca.created_by chair_attribute_created_by,
                ca.chair_attribute_id
            from chair
                left join chair_attribute ca on
                    ca.chair_id = chair.chair_id
                left join system_attribute sa on
                    sa.system_attribute_id =
                        ca.system_attribute_id
            where chair.chair_id = @chair_id
        else -- we want a particular said only
            select 
                chair_id,
                chair.modified_on chair_modified_on,
                chair.modified_by chair_modified_by,
                chair.created_on chair_created_on,
                chair.created_by chair_created_by,
                chair_attribute_value,
                system_attribute_name,
                system_attribute_description,
                system_attribute_display_text,
                system_attribute_display_order,
                ca.modified_on chair_attribute_modified_on,
                ca.modified_by chair_attribute_modified_by,
                ca.created_on chair_attribute_created_on, 
                ca.created_by chair_attribute_created_by,
                ca.chair_attribute_id
            from chair
                left join chair_attribute ca on
                    ca.chair_id = chair.chair_id
                left join system_attribute sa on
                    sa.system_attribute_id =
                        ca.system_attribute_id
            where
                chair.chair_id = @chair_id and
                ca.system_option_id = @chair_said
    

    This stored procedure gives us either a list of all of the attributes for a chair, or we can optionally request a specific attribute from the database.

    create procedure chair_attribute_insert
        @chair_id bigint,
        @said bigint,
        @chair_attribute_value varchar(8000),
        @created_by bigint,
        @chair_attribute_id bigint output,
        @error_text varchar(500) output
    as
    
    /* first we need to test that the 
    passed value is legal  */
    
    -- is there a verify for this said?
    declare @sp_name varchar(255), 
        @sp_text varchar(500),
        @verify_status
    
    select @sp_name = system_attribute_verify_sql
        from system_attribute
        where system_attribute_id = @said
    
    /* 
    if sp_name is filled, then we have something to test
    all of the verify stored procedures take one value
    and return a 0 or a 1.  0 means the value is OK,
    1 means the value failed.
    The procedures take 2 parameters, the value in and
    an output parameter with the error text.
    */
    if @sp_name <> ''  begin
        exec @verify_status = @sp_name
          @chair_attribute_value, @sp_text output
        if @verify_status = 1  begin
            -- we have a failure in the verify process
            set @error_text = @sp_text
            return 1
    end
    
    -- we were either successful in the verify
    -- or didn't need to
    insert into chair_attribute
     (chair_id, chair_attribute_value, system_attribute_id,
      created_by, created_on)
     values
     (@chair_id, @chair_attribute_value, @said,
      @created_by, getdate())
        if @@ERROR = 0
            set @chair_attribute_id = @@IDENTITY
            -- mark the chair itself as modified
            exec chair_update @chair_id, @created_by
    end -- end verify_status = 0
    

    As you can see, this stored procedure gets much more complex. We now have to worry about getting the verification procedure, if it exists, from the database and run that. If that is successful, then we can proceed on with the insert process.

    If it isn't, we need to record the text from the procedure and pass that back to our application as an intelligent response back to the user. Things like: "Error out of range. Only a number between 1 and 10 is allowed" would be an example of the returned string. The application can then use this and display it to the user so that they can fix their values.

    We also need to touch the parent item (the chair in this case) and set the modified information.

    Finally, we exit and return 0 back to the calling application.

    create procedure chair_attribute_update
        @chair_attribute_id bigint,
        @chair_id bigint,
        @said bigint,
        @modified_by bigint,
        @error_text
    as
        /* We need to verify the value first */
        declare @sp_name varchar(255), 
            @sp_text varchar(500),
            @verify_status
    
    select @sp_name = system_attribute_verify_sql
        from system_attribute
        where system_attribute_id = @said
    
    /* 
      if sp_name is filled, then we have something to
      test all of the verify stored procedures take
      one value and return a 0 or a 1.  0 means the
      value is OK, 1 means the value failed.
      The procedures take 2 parameters, the value in
       and an output parameter with the error text.
    */
    if @sp_name <> ''  begin
        exec @verify_status = @sp_name
          @chair_attribute_value, @sp_text output
        if @verify_status = 1  begin
            -- we have a failure in the verify process
            set @error_text = @sp_text
            return 1
    end
    
        /* if we made it this far, we're OK */
        update chair_attribute
        set
            chair_attribute_value = @chair_attribute_value,
            chair_id = @chair_id,
            system_attribute_id = @said,
            modified_by = @modified_by,
            modified_on = @modified_on
        where
            chair_attribute_id = @chair_attribute_id
    
        /* we need to touch the parent */
        exec chair_update @chair_id, @modified_by
    
        -- we can simply return the @@ERROR here
        return @@ERROR
    

    This is very similar to the insert procedure. We need to ensure the passed values are legal and that then we can update the item. We need to pass in the id of the current attribute item and then we can change any and all of the items on the record.

    Again, we need to touch the parent item (the chair) so that we track the modified time and user.

    create procedure chair_attribute_delete
        @chair_attribute_id bigint,
        @modified_by bigint
    as
    
        /* we need to get the parent item before we continue */
        declare @chair_id bigint
        select @chair_id = chair_id from chair_attribute
          where chair_attribute_id = @chair_attribute_id
    
        delete chair_attribute
          where chair_attribute_id = @chair_attribute_id
        if @@ERROR = 0
            exec chair_update @chair_id, @modified_by 
        return @@ERROR
    

    The delete procedure first determines the current parent item of the record we want to delete, then it deletes the record and touches the parent.

    What's Next?

    In the next article, we'll dig into the application side of the model. We'll discuss building the user interface and how to take advantage of the Visual Studio tools to quickly and easily access the data.

    We'll also get into the verification stored procedures and how those are set up and how they can be used.

    About the Author

    Peter Scheffler is an independent consultant from Ontario Canada. He specializes in developing back office solutions around business critical database applications. He has developed applications using many different database technologies (SQL/Server, Oracle, mySQL and Access) and enabling technologies (ASP/ASP.NET, VB/VB.NET, C/C++, php and Java.)

    Prior to being a full-time independent consultant, Peter spent more than 10 years in the enterprise software development community as a developer, pre-sales/post-sales consultant and in various product management positions.

    When he's not busy hacking at his keyboard, he's busy at the hockey rink with his kids or camping in his house-on-wheels. You can read about his misadventures in his Camping Blog at http://www.scheffler-consulting.com.

    Peter can be reached at peter@scheffler-consulting.com.

  • Rate This Article
    Not HelpfulMost Helpful
    1 2 3 4 5
    Other Articles
    Aug 31, 2005 - The X-Factor in SOA
    In this article, Joseph Poozhikunnel examines the importance of the three X's -- namely XML, XML Schema, and XSLT -- in a service oriented architecture (SOA). He then defines the design considerations that need to be adopted when designing a system based on SOA and examines the pitfalls that can arise if they're not followed.
    [Read This Article]  [Top]
    May 19, 2005 - Building an Enterprise Service Bus to Support Service Oriented Architecture
    In this article, Joseph Poozhikunnel defines an Enterprise Service Bus (ESB) that can be created to support any Service Oriented Architecture (SOA) adopted by an organization. The type of ESB required could vary as there is no "one size fits all", therefore the article examines a few of the mechanisms available that could be adopted to implement an ESB.
    [Read This Article]  [Top]
    Mar 24, 2005 - Building an End User Defined Data Model - Part 1
    In the first article in this series, Peter Scheffler introduces the concept of a rules-based database engine that allows clients to make changes to their database structure without breaking the applications that access the database.
    [Read This Article]  [Top]
    Jan 19, 2005 - Developing a Simple Service Oriented Architecture
    The basic premise of a Service Oriented Architecture (SOA) system is to decouple applications from each other in order to make them autonomous. In this article, Joseph Poozhikunnel presents a simple SOA framework that can be used as a starting point for a system that addresses your specific business needs.
    [Read This Article]  [Top]
    Nov 3, 2004 - 10 Steps to a Successful Versioning and Deployment Strategy for .NET
    A well rounded versioning and deployment strategy considers several overlapping and interdependent .NET Framework concepts. In this article, Michele Leroux Bustamante will take you through a ten step program that reviews these core concepts, their relationship, and provides guidance for successful application deployments for the .NET Framework.
    [Read This Article]  [Top]
    Oct 27, 2004 - Business Intelligence with Microsoft SQL Server Reporting Services - Part 2
    Adnan Masood continues his discussion of Microsoft SQL Server Analysis services and Microsoft SQL Server Reporting services. In this part, he discusses the steps that go into building more advanced reports.
    [Read This Article]  [Top]
    Oct 13, 2004 - Business Intelligence with Microsoft SQL Server Reporting Services - Part 1
    Adnan Masood discusses Microsoft's comprehensive integrated business intelligence, data mining, analysis and reporting solution: Microsoft SQL Server Analysis services and Microsoft SQL Server Reporting services.
    [Read This Article]  [Top]
    Dec 15, 2003 - Realizing a Service-Oriented Architecture with .NET
    Chip Irek examines the architectural issues and component design issues of building a .NET application in a service-oriented architecture.
    [Read This Article]  [Top]
    Oct 21, 2003 - Achieving Reuse in ASP .NET - Part 1: Barriers to Reuse
    The importance of reuse can't be overstated, especially in light of the degree to which we go out of our way to avoid it, but implementing a reuse strategy means creating high-quality low-cost applications that just might save your job.
    [Read This Article]  [Top]
    Jun 16, 2003 - The .NET Architect: Enterprise Template Dynamic Help
    One of the most critical components of any application is the help file collection. The fourth article in Brian Korzeniowski's Enterprise Template series examines Dynamic Help in Visual Studio .NET and focuses on the logical process of creating help content.
    [Read This Article]  [Top]
    Mailing List
    Want to receive email when the next article is published? Just Click Here to sign up.

    Support the Active Server Industry



    JupiterOnlineMedia

    internet.comearthweb.comDevx.commediabistro.comGraphics.com

    Search:

    Jupitermedia Corporation has two divisions: Jupiterimages and JupiterOnlineMedia

    Jupitermedia Corporate Info


    Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

    Advertise | Newsletters | Tech Jobs | Shopping | E-mail Offers