Question

We need to store "Setup" structures in an SQL Server Database. A "Setup" has multiple "channels" which can have subchannels. Every channel can have multiple "variables" containing standard attributes with values.

This structure is defined in a C# project. So, there is a Setup object containing a List, there is a Channel object containing a List and a List.

I have actually completed the project by creating a Setup table, a Channel table with a foreign key to Setup and a self-reference, and a Variable table with a foreign key to channels.

The c# program needs some functionality to list existing setups, load, and save.

I created a stored procedure to save one setup that accepts 3 table parameters: Setup (should contain just one row), Channel, and Variable. The input parameters contain only the natural keys of the entities. Then the stored procedure inserts or updates them into the db.

For the loading of one setup, I created a stored procedure that executes 3 selects. Then the c# reads them through SqlCommand.ExecuteReader().

Question: The save setup stored procedure is damn too complicated. Here are briefly the steps. It all gets much more complicated because of the hierarchical structure of the Channels, because I need to process step by step for each level of the hierarchy:

Parameter validity check

Check if Setup exists
    if yes, update, keep its id (SetupID)
    if not, insert, keep its id (SetupID)

Update SetupID in parameter tables Channel,Variable
Fetch ChannelID for channels given in parameters that do exist in the database
Fetch VariableID for variables given in parameters that do exist in the database

Delete channels and variables that do exist in database for that setup, but were not provided in the parameters
    Channels should be recursively deleted, first the ones without children, then their parents etc.

Insert new Channels (given in parameters, not existing in the database)
    Do it recursively, first for the ones without children, then, their parents, etc.
    Every time you do some insert, update the respective ChannelID in the parameter tables
Update Channels given in parameters that do exist in the database

Insert new Variables (given in parameters, not existing in the database)
Update Variables given in parameters that do exist in the database

It translates to about 250 lines of T-SQL code that took me 2 days to write and error check. It works fine now, but I am sure it can be done in a simpler way. Any ideas/comments?

P.S.: Saving in XML is not an option, because the key idea of the project is to be able to view all the properties and make queries upon them, verify, make reports and statistics, etc.

Thanks in advance!

Update: Table creation script

CREATE TABLE [dbo].[Setup](
    [SetupID] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NOT NULL,
    [Type] [nvarchar](4000) NULL,
    [Info] [nvarchar](4000) NULL,
    [FirstInserted] [datetime] NULL,
    [LastUpdated] [datetime] NULL,
 CONSTRAINT [PK$Setup] PRIMARY KEY CLUSTERED 
([SetupID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE UNIQUE NONCLUSTERED INDEX [UQ$Setup$Name] ON [dbo].[Setup] 
([Name] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO

CREATE TABLE [dbo].[Channel](
    [ChannelID] [int] IDENTITY(1,1) NOT NULL,
    [SetupID] [int] NOT NULL,
    [Type] [nvarchar](50) NOT NULL,
    [ParentChannelID] [int] NULL,
    [Sequence] [int] NOT NULL,
    [PanelIdx] [nvarchar](4000) NULL,
    [Visible] [nvarchar](4000) NULL,
    [FirstInserted] [datetime] NULL,
    [LastUpdated] [datetime] NULL,
 CONSTRAINT [PK$Channel] PRIMARY KEY CLUSTERED 
([ChannelID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Channel]  WITH CHECK ADD  CONSTRAINT [FK$Channel$Channel] FOREIGN KEY([ParentChannelID])
REFERENCES [dbo].[Channel] ([ChannelID])
GO
ALTER TABLE [dbo].[Channel] CHECK CONSTRAINT [FK$Channel$Channel]
GO
ALTER TABLE [dbo].[Channel]  WITH CHECK ADD  CONSTRAINT [FK$Channel$Setup] FOREIGN KEY([SetupID])
REFERENCES [dbo].[Setup] ([SetupID])
GO
ALTER TABLE [dbo].[Channel] CHECK CONSTRAINT [FK$Channel$Setup]
GO
CREATE UNIQUE NONCLUSTERED INDEX [UQ$Channel$SetupID_Type] ON [dbo].[Channel] 
([SetupID] ASC,[Type] ASC )
WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO
CREATE UNIQUE NONCLUSTERED INDEX [UQ$Channel$SetupID_ParentChannelID_Sequence] ON [dbo].[Channel] 
([SetupID] ASC, [ParentChannelID] ASC, [Sequence] ASC )
WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO

CREATE TABLE [dbo].[Variable](
    [VariableID] [int] IDENTITY(1,1) NOT NULL,
    [ChannelID] [int] NOT NULL,
    [Key] [nvarchar](50) NOT NULL,
    [Sequence] [int] NOT NULL,
    [DefaultText] [nvarchar](4000) NULL,
    [IONumber] [nvarchar](4000) NULL,
    [LinkType] [nvarchar](4000) NULL,
    [DataType] [nvarchar](4000) NULL,
    [ImageTrue] [nvarchar](4000) NULL,
    [ImageFalse] [nvarchar](4000) NULL,
    [FormatString] [nvarchar](4000) NULL,
    [GroupBoxIdx] [nvarchar](4000) NULL,
    [ControlIdx] [nvarchar](4000) NULL,
    [PlcVar] [nvarchar](4000) NULL,
    [Value] [nvarchar](4000) NULL,
    [DefaultValue] [nvarchar](4000) NULL,
    [MinValue] [nvarchar](4000) NULL,
    [MaxValue] [nvarchar](4000) NULL,
    [Measure] [nvarchar](4000) NULL,
    [KeyIdx] [nvarchar](4000) NULL,
    [Behavior] [nvarchar](4000) NULL,
    [TrueEnter] [nvarchar](4000) NULL,
    [ShowCheckDigit] [nvarchar](4000) NULL,
    [ShowOverflow] [nvarchar](4000) NULL,
    [Visible] [nvarchar](4000) NULL,
    [ReadOnly] [char](1) NULL,
    [Dynamic] [char](1) NULL,
    [FirstInserted] [datetime] NULL,
    [LastUpdated] [datetime] NULL,
 CONSTRAINT [PK$Variable] PRIMARY KEY CLUSTERED 
([VariableID] ASC)
WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE UNIQUE NONCLUSTERED INDEX [UQ$Variable$ChannelID_Key] ON [dbo].[Variable] 
([ChannelID] ASC, [Key] ASC)
WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO
CREATE UNIQUE NONCLUSTERED INDEX [UQ$Variable$ChannelID_Sequence] ON [dbo].[Variable] 
([ChannelID] ASC, [Sequence] ASC )
WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Variable]  WITH CHECK ADD  CONSTRAINT [FK$Variable$Channel] FOREIGN KEY([ChannelID])
REFERENCES [dbo].[Channel] ([ChannelID])
GO
ALTER TABLE [dbo].[Variable] CHECK CONSTRAINT [FK$Variable$Channel]
GO
ALTER TABLE [dbo].[Variable]  WITH CHECK ADD  CONSTRAINT [CK$ChannelVariable$Dynamic_TF] CHECK  (([Dynamic]='T' OR [Dynamic]='F'))
GO
ALTER TABLE [dbo].[Variable] CHECK CONSTRAINT [CK$ChannelVariable$Dynamic_TF]
GO
ALTER TABLE [dbo].[Variable]  WITH CHECK ADD  CONSTRAINT [CK$ChannelVariable$ReadOnly_TF] CHECK  (([ReadOnly]='T' OR [ReadOnly]='F'))
GO
ALTER TABLE [dbo].[Variable] CHECK CONSTRAINT [CK$ChannelVariable$ReadOnly_TF]
GO
Was it helpful?

Solution

Look into using an Object Relational Mapper.

Some examples in .Net are Entity Framework (from Microsoft), NHibernate (open source), and LLBLGen (commercial).

There are others as well, and each has their pros and cons.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top