开发者

The Link Between Webform Combobox Data and the Database (SQL Server & ASP.NET)

开发者 https://www.devze.com 2023-04-05 17:45 出处:网络
The title, while long, pretty much says it all. What I have is a master table with a bunch of supporting table relations through foreign keys. For a few of the foreign tables, upon attempting to inse

The title, while long, pretty much says it all.

What I have is a master table with a bunch of supporting table relations through foreign keys. For a few of the foreign tables, upon attempting to insert a record into the master table where one of the foreign keys doesn't exist, the data would be passed to the foreign table to create the record first, thereby making the constraint valid and passing the key to the created record back to the insert procedure of the master table.

This data comes from a form in String form, but naturally the foreign key will be an int. The process would look something like this:

-- ASP.NET Web Form --
Requestor Name:    _____________ (combobox)
Request:           _____________ (dropdownlist)
Date:              _____________ (datepicker)

This is a super simplified version, but assume we have a master table with the above data, where both names are foreign keys to a People table. The name fields are comboboxes with a populated list of names linking to People. However, if I wanted to enter a person who didn't yet exist in the People table, the procedure should first create the Person, then use the ID from that new record as the foreign key in the Master table containing columns for the above.

I'm using SQL Server and ASP.NET with VB.NET codebehind. I've been scratching my head over this one for awhile, how to pass data (in different forms such as a foreign key or string) between the web server and DB server, as well as where to validate / transform the data.

It seems the开发者_StackOverflow社区 entered name will be passed as an ID if the foreign key exists, and a String if not.

This is my most perplexing problem so far, and no idea where else to look. I've read up on Scott Mitchell's site and others.

MY SOLUTION (?)

The best I can come up with is to pass the user input from the user as a string and convert it to int in the T-SQL procedure. If the value was selected from the drop down, it should match precisely with a valid foreign key. If it doesn't match, then create a new Person and return a foreign key. Is this best practice?


This seems complicated because it is. You have to get your hands dirty. If you need a relational database with ACID support, there's no auto-magical way of getting around it.

Relational databases 101: The primary key must exist before the foreign key can be populated (This is the reason why data warehouse developers populate the dimension table before the fact table). You'll have to design the logic to validate that the primary key exists, insert and get the key if not, and just get the key if exists.


Here's my implementation. I don't know if it's the best, but it worked well for me. Basically I take the values from the controls; in the case of the combobox I need the values from both the TextBox and DropDownList. I then pass those values to the following function in my codebehind:

'This method determines if the name selected already exists in the selection
'  options and if so assigns the corresponding ID value to an object variable,
'  if not it assigns the value of the `TextBox` to the variable.
Protected Function _ValidateValues(ByRef ddl As DropDownList, ByRef cb As TextBox) As Object
    'Ensures the selected value is valid by checking against the entered value in the textbox
    If Not String.IsNullOrEmpty(cb.Text) Then
        If ddl.Items.Count > 0 Then
            If StrComp(cb.Text, ddl.SelectedItem.ToString) = 0 Then
                Return ddl.Items.Item(ddl.SelectedIndex).Value 'Returns the index of dropdown selected name
            End If
        End If
        'This counts the capital letters in the entered value and if fewer than 2
        '  auto capitalizes the first letters. This also allows for project code
        '  names such as "DOORS" and people names such as "Allen McPherson" etc.
        '  Be careful though because if "allen McPherson" is entered, it will NOT
        '  be corrected, though it displays correctly.
        Dim rg As New Regex("[A-Z]")
        Dim mc As MatchCollection = rg.Matches(cb.Text)
        If mc.Count < 2 Then
            Return StrConv(cb.Text, VbStrConv.ProperCase)
        Else : Return cb.Text
        End If
    End If
    'Returns a SQL DB NULL object if an empty string is submitted
    Return DBNull.Value
End Function

Then my stored procedure handles the values something like so...

(Forgive me if I neglected to replace some of the values. I tried to catch them all.)

CREATE PROCEDURE spInsertUser
    @User nvarchar(50)    = NULL,
    @Role nvarchar(50)    = NULL,
    @RecordID int output  -- Returned Value
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- CHECK IF USER EXISTS
    -- Insert new record to Users table if the requested user doesn't exist
    -- Needed to ensure that the foreign keys are relevant
    IF @User = '' OR @User IS NULL BEGIN SET @User = NULL SET @RecordID = NULL END --Ensures that an empty string cannot be submitted, thereby causing an error.
    ELSE BEGIN
        declare @forename varchar(50), @surname varchar(50)
        declare @uid table (ID int)
        declare @users table (ID smallint, Name nvarchar(50))
        insert into @users
        select ID, Name from Users

        --If the value can be converted into an int, we need go no further.
        BEGIN TRY SET @RecordID = CONVERT(smallint, @User) END TRY
            BEGIN CATCH
                BEGIN TRY --Otherwise, attempt to parse the name
                    Set @User = LTRIM(RTRIM(@User)) --Trim the extra space at the beginning and end. This ensures the following "IF" test will evaluate properly.
                    IF NOT CHARINDEX(' ', @User) > LEN(@User) AND CHARINDEX(' ', @User) > 0 BEGIN -- Confirm First & Last Name exist
                        Set @forename = RTRIM(LEFT(@User, CHARINDEX(' ',@User,0)-1))
                        Set @surname = LTRIM(RIGHT(@User, LEN(@User) - CHARINDEX(' ',@User,0)))
                        Set @User = @forename + ' ' + @surname --Ensure that there is a valid First & Last name
                        IF LEN(@forename) > 1 AND LEN(@surname) > 1 BEGIN -- Confirm First & Last Name exist
                            --First ensure that the User doesn't already exist, and if
                            --  so use their ID, if not insert the new User.
                            IF NOT EXISTS (select Name from @users where Name like @User) BEGIN --Check if the user already exists
                                INSERT INTO Users (Name, Forename, Surname) OUTPUT INSERTED.ID INTO @uid Values (@User, -- If not, insert them
                                    @forename, @surname) --Nicely manicured first, last, and full names
                                SET @RecordID = CONVERT(smallint, (select MAX(ID) from @uid)) END -- Now set the Role to the ID of the new user
                            ELSE BEGIN --Otherwise if the user already exists, set the Role to the ID of that user
                                SET @RecordID = (select ID from @users where Name like @User) END

                            IF NOT EXISTS (select * from rUsersInRoles where UserID = @RecordID) BEGIN
                            --Do some string manipulation to increase the chances of matching the role
                            SET @Role = REPLACE(REPLACE(REPLACE(LTRIM(RTRIM(@Role)), ' ', '%'), '.', '%'), '@', '%') --Trims & replaces spaces & periods with wildcards
                            INSERT INTO rUsersInRoles (UserID, UserRoleID) VALUES
                                (@RecordID, (select top 1 ID from rUserRoles where Role like @Role)) END
                        END
                    END
                END TRY
                BEGIN CATCH END CATCH    
            END CATCH
    END
END

This stored procedure deals with the case of User Roles as well. If the more simple case of Users only is needed, simply remove the clauses dealing with the checking and insertion of User Roles. :)

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号