You are here:   Blog
Register   |  Login

 

Feb 8

Written by: Richard Waddell
2/8/2013 11:51 PM  RssIcon

image

LightSwitch provides for creating Data Sources in several ways. In the case of an existing database you would typically either specify the database itself or a WCF RIA Service.

image

But sometimes neither is feasible. I was confronted with a database with no primary keys and incredibly wide tables – so wide that I got error messages about it. And you can’t really do much with a database that has no primary keys. Also modifying the schema was not an option.

This actually turned out to be quite liberating. The reason we wanted to access the database outside of the legacy application it was designed for was to

  • Provide views of the data that the legacy application did not.
  • Add additional columns

By extracting the data into another database we could

  • Eliminate literally hundreds of columns (Yes, I do mean literally)
  • Create primary keys
  • Create relationships with referential integrity through foreign keys

So what does the legacy database look like? Sorry, that’s proprietary, or at least I’m not taking any chances. Fortunately, it just doesn’t matter, the principles are the same for any situation. So I made up a very simplistic database I call ShoppingCenterMgmt. The fictional "Shopping Center Management” application it supports is so old that going to the mall was unknown unless you were in Washington D.C. You can’t get much more legacy than that.

It consists of three tables

  • shopping_centers
  • stores
  • customers

Each shopping center exists at a specific physical location and contains one or more stores, which is identified by unit within shopping_center_id. If a store currently has an occupant it’s indicated by an entry in the customer_id field.

image

As you can see there are no primary keys, let alone foreign keys, therefore no referential integrity. The only thing the DBMS will prevent is duplicate unique keys. There’s nothing to prevent deletion of shopping centers that have stores or customers who currently occupy stores. Or you could add a store with an invalid shopping_center_id or customer_id. The opportunities are endless.

So we would like to not only capture the data in the columns shown above for these tables, but we would also like to add primary and foreign keys so we can achieve referential integrity and everything that goes with it. The first thing we need are target tables to import into.

The schema for the shopping_centers table looks like this:

image

We’ll create a table named Malls to receive the data.

image

We retain shopping_center_id as a unique index and add a Summary field which I’ll explain later.

Before moving on to the other tables, lets look at how the import works. We could write a stored procedure that specifically handles copying data from the shopping_centers table to the Malls table, but in a real-world scenario we’ve got several tables and a lot of columns so that will get pretty tedious. Far better to create tables to hold the desired table and column names and the screens to enter them.

It’s pretty simple, really, if you just take it one step at a time. To start with, we know we have to specify the name of the legacy table name, so we begin with that.

image

To begin with we just provide a column for the table name and require that it be unique.

We create a table name LegacyShadowTables for the target table names.

image

Now we need a way to pair up each legacy table with the appropriate shadow table.

image

This creates a many-to-many relationship between LegacyTable and LegacyShadowTable. Although our mapping will always be one-to-one, it doesn’t have to be.

Now we add a computed property ‘Tables’ to LegacyShadowTablePair.

public partial class LegacyShadowTablePair
{
    partial void Tables_Compute(ref string result)
    {
        result = this.LegacyTable != null ? this.LegacyTable.ToString() : "?";
        result += " -> ";
        result += this.LegacyShadowTable != null ? this.LegacyShadowTable.ToString() : "?";
    }
}

NOTE: Since I’m still running under debug, all the data I’m entering over the next few screens will be going into the debug database. This means it will have to be re-entered into the ‘real’ database after publishing. There’s not very much, but you may want to wait until then to enter it.

Starting with the Legacy Tables List Detail screen, we enter the first name, shopping_centers.

image

On the Legacy Shadow Tables List Detail screen, enter the  first shadow table name, Malls.

image

And create our first shadow table pair:

image

You’ve probably noticed there’s no validation of the table names that are entered. That’s more trouble than it’s worth In this initial effort to get the import up and running. Any errors in table or column names will result in exceptions that will be logged when the stored procedure runs.

Here are some examples from when I was debugging my attempts to get the procedure working:

image

Ok, we have our first pair defined and three convenient screens for visualizing the relationships:

image

image

image

Next we need to specify the column mappings between the tables.

image

Each Leg

acyShadowTablePair has multiple pairs of column mappings, so there’s a one-to-many relationship between LegacyShadowTablePair and LegacyShadowColumnPair. To prevent duplicating the column pairs for a particular table pair, LegacyShadowTablePair, LegacyTableColumn, and ShadowTableColumn are specified as ‘Include in Unique Index’. The Columns computed property is analogous to the ‘Tables’ computed column in the LegacyShadowTablePair table.

We can add a LegacyShadowColumnPair screen to enter the column names, but it’s far more convenient to use the Legacy Shadow Table Pair List Detail Screen. To do so you’ll need to modify the screen by Adding the Legacy Shadow Column Pairs collection to the screen

image

Click the line shown above, then drag the resulting query onto the screen.

image

Now we can maintain the column names by table pairs.

image

At this point we’re going to need to publish so we have a SQL database to import the data into. Strictly speaking you could use a different database as the target and continue to

run LightSwitch under debug, but if you want to use the LightSwitch database for the data then you have to publish now.

We’re going to set Access Control to ‘Use Forms Authentication’. This will cause the publishing process to create a Site Administrator logon.

image

You publish from the Application Type selection.

image

We’re hosting on IIS Server and I have the Prerequisites installed, which I recommend:

image

Note: If you have problems try unchecking the ‘IIS Server has the LightSwitch Deployment Prerequisites installed’. I was tearing my hair out last night trying to figure out why my Application Administrator settings were not being applied. I still don’t know if having this checked was the cause, because now it works fine both ways.

I publish directly to the server whenever possible:
image

HTTPS off, unless you want it on:

image

The easiest approach is to create the new database during the publish process. The User ID must be a login that serves in public and sysadmin server roles. As a note of interest, after publishing, if you take a look with SSMS, the database owner will show as dbo. If you look at the mapping for the login you specified, you will see that for the database you created, dbo maps to that login.

image

Application Administrator fields. You only have to do this once:

image

Note: If your credentials don’t work when you try to log in take a look at web.config as shown below. The comment “<!—If no admin user exists…” is incorrect. It should say, “<!—If no admin user with this user name exists…”. Don’t bother looking in the database right after you publish – it’s only during the first log on after you publish that these settings are used to update the database.

<configuration>
  <appSettings>
   ...    
    <!-- If no admin user exists, create an admin user with this user name -->
    <add key="Microsoft.LightSwitch.Admin.UserName" value="MallsMgr2" />
    <!-- When creating an admin user with Forms auth, use this as the full name -->
    <add key="Microsoft.LightSwitch.Admin.FullName" value="MallsMgr2 Name" />
    <!-- When creating an admin user with Forms auth, use this as the pasword -->
    <add key="Microsoft.LightSwitch.Admin.Password" value="MallsMgr12345/" />
    ...
  </appSettings>
...
</configuration>

Create a test certificate:

image

Your summary screen should look something like this:

image

Go ahead and click on the Publish button.

NOTE: If you have problems publishing check out this Beth Massi article. It’s from 6/8/2012 but seems to have the most recent information that isn’t just a description of each Wizard step, such as this article, helpful to 0 out of 1. Sigh.

Let’s start simple by writing a simple stored procedure to just copy the rows from the shopping_centers table to the Malls table:

USE [LS_Malls3]
GO
/****** Object:  StoredProcedure [dbo].[uspCopy_ShoppingCentersToMallsTable]    Script Date: 02/03/2013 09:17:17 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[uspCopy_ShoppingCentersToMallsTable] 
AS
BEGIN
    SET NOCOUNT ON;
    
	BEGIN TRY
		DELETE Malls
		INSERT INTO Malls (shopping_center_id, name, addr1, addr2, city, state, zip_code)
		SELECT shopping_center_id, name, addr1, addr2, city, state, zip_code
		FROM [ShoppingCenterMgmt].[dbo].[shopping_centers]
    END TRY
	BEGIN CATCH
        EXECUTE [dbo].[uspPrintError];
        IF @@TRANCOUNT > 0
        BEGIN
            ROLLBACK TRANSACTION;
        END
        EXECUTE [dbo].[uspLogProcError];
	END CATCH
END;

There are two procedures to handle error reporting. I can’t remember where I got them, it may have been from here:

USE [LS_Malls3]
GO
/****** Object:  StoredProcedure [dbo].[uspPrintError]    Script Date: 02/03/2013 09:23:07 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- uspPrintError prints error information about the error that caused 
-- execution to jump to the CATCH block of a TRY...CATCH construct. 
-- Should be executed from within the scope of a CATCH block otherwise 
-- it will return without printing any error information.
CREATE PROCEDURE [dbo].[uspPrintError] 
AS
BEGIN
    SET NOCOUNT ON;
    -- Print error information. 
    PRINT 'Error ' + CONVERT(varchar(50), ERROR_NUMBER()) +
          ', Severity ' + CONVERT(varchar(5), ERROR_SEVERITY()) +
          ', State ' + CONVERT(varchar(5), ERROR_STATE()) + 
          ', Procedure ' + ISNULL(ERROR_PROCEDURE(), '-') + 
          ', Line ' + CONVERT(varchar(5), ERROR_LINE());
    PRINT ERROR_MESSAGE();
END;

USE [LS_Malls3]
GO
/****** Object:  StoredProcedure [dbo].[uspLogProcError]    Script Date: 02/03/2013 09:24:48 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- uspLogError logs error information in the ErrorLog table about the 
-- error that caused execution to jump to the CATCH block of a 
-- TRY...CATCH construct. This should be executed from within the scope 
-- of a CATCH block otherwise it will return without inserting error 
-- information. 
CREATE PROCEDURE [dbo].[uspLogProcError] 
    @ErrorLogID [int] = 0 OUTPUT -- contains the ErrorLogID of the row inserted
AS                               -- by uspLogError in the ErrorLog table
BEGIN
    SET NOCOUNT ON;
    -- Output parameter value of 0 indicates that error 
    -- information was not logged
    SET @ErrorLogID = 0;
    BEGIN TRY
        -- Return if there is no error information to log
        IF ERROR_NUMBER() IS NULL
            RETURN;
        -- Return if inside an uncommittable transaction.
        -- Data insertion/modification is not allowed when 
        -- a transaction is in an uncommittable state.
        IF XACT_STATE() = -1
        BEGIN
            PRINT 'Cannot log error since the current transaction is in an uncommittable state. ' 
                + 'Rollback the transaction before executing uspLog_FAST_Error in order to successfully log error information.';
            RETURN;
        END
        INSERT [dbo].[ProcErrorLog] 
            (
            [ErrorTime],
            [UserName], 
            [ErrorNumber], 
            [ErrorSeverity], 
            [ErrorState], 
            [ErrorProcedure], 
            [ErrorLine], 
            [ErrorMessage]
            ) 
        VALUES 
            (
            GETDATE(),
            CONVERT(sysname, CURRENT_USER), 
            ERROR_NUMBER(),
            ERROR_SEVERITY(),
            ERROR_STATE(),
            ERROR_PROCEDURE(),
            ERROR_LINE(),
            ERROR_MESSAGE()
            );
        -- Pass back the ErrorLogID of the row inserted
        SET @ErrorLogID = @@IDENTITY;
    END TRY
    BEGIN CATCH
        PRINT 'An error occurred in stored procedure uspLogError: ';
        EXECUTE [dbo].[uspPrintError];
        RETURN -1;
    END CATCH
END;

And an error log table:

image

We’ll create a test row by manually inserting one into the shopping_centers table:

image

Then manually execute the stored procedure:

image

And the results:

image

Which can be seen in the Malls List Detail screen:

image

Since we’re deleting the contents of the Malls table before we do the inserts, we can run the stored procedure over and over again. But let’s see what happens if we take out the Delete and try to run it more than once.

The return value from the procedure is now –4:

image

And the  Messages tab shows that we’re trying to insert a duplicate in the Malls table.

image

Here’s the row in ProcErrorLog:

image

We can add the ProcErrorLog table to LightSwitch:

image

And a screen to view it:

image

Now we’ll replace the insert with dynamic sql using hard-coded values:

USE [LS_Malls3]
GO
/****** Object:  StoredProcedure [dbo].[uspCopy_ShoppingCentersToMallsTable]    Script Date: 02/03/2013 10:43:10 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[uspDynamicCopy_ShoppingCenters1] 
AS
BEGIN
    SET NOCOUNT ON;
	DECLARE @CR NVARCHAR(1) = CHAR(13)
	DECLARE @DIVIDER NVARCHAR(25) = @CR + '-------------------------' 
	DECLARE @debug bit = 0
	DECLARE @sql nvarchar(MAX);
	DECLARE @targetTableName nvarchar(255) = N' Malls '
	DECLARE @theTargetColumns nvarchar(MAX);
	DECLARE @theSourceColumns nvarchar(MAX);
	DECLARE @FROM_CLAUSE nvarchar(MAX) = N' FROM [ShoppingCenterMgmt].[dbo].[shopping_centers] '
    
	BEGIN TRY
		DELETE Malls
		Set @theTargetColumns = N' shopping_center_id, name, addr1, addr2, city, state, zip_code ';
		Set @theSourceColumns = N' shopping_center_id, name, addr1, addr2, city, state, zip_code '; 
		
        SET @sql = N' INSECT ' + @targetTableName + @CR +
        N' ( ' + @theTargetColumns + N' ) ' + @CR +
        N' SELECT ' + @theSourceColumns + @CR +
        @FROM_CLAUSE
        
		----------- DEBUG --------------
		IF @debug = 1 PRINT 'Insert: ' + @DIVIDER + @CR + @sql + @DIVIDER + @CR
		---------------------------------
        EXEC sp_executesql @sql
		
    END TRY
	BEGIN CATCH
        EXECUTE [dbo].[uspPrintError];
        -- Rollback any active or uncommittable transactions before
        -- inserting information in the ErrorLog
        IF @@TRANCOUNT > 0
        BEGIN
            ROLLBACK TRANSACTION;
        END
        EXECUTE [dbo].[uspLogProcError];
        RETURN -1;
	END CATCH
END;
When we execute the procedure, we get a Return Value of –1, indicating an error:

image

The Messages tab is not much help:

image

Fortunately, we can use PRINT to see more. If you look near the beginning of the stored procedure you’ll see

	DECLARE @debug bit = 0

Further down, after @sql has been formatted you see

		----------- DEBUG --------------
		IF @debug = 1 PRINT 'Insert: ' + @DIVIDER + @CR + @sql + @DIVIDER + @CR
		---------------------------------

So if we set @debug to 1 instead of zero and run the procedure again we can see a display of the T-SQL code executed by sp_executesql, resulting in the Incorrect syntax error.

image

As you can see, we mis-spelled INSERT, so we fix it and run it again.

image

The code looks right and we don’t get any errors when it’s executed.

Ok, we have some working dynamic sql and we can use PRINT to debug errors. Now let’s start making it table driven instead of just plugging in hard-coded values. First we’ll add another table pair to make it a little more interesting. We add another legacy table, stores.

image

Our Malls don’t have any mundane old stores, they have Shops

image

So we’re mapping the stores table from out tired old shopping_centers database to the Shops table in our new, scintillating LS_Malls3 database. (The database names can also be table-driven, but for now we just hard-code them).  Below we see that we’ve paired up stores with Shops.

image

Let’s take a look at the actual tables.

image

image

You can see below that the foreign keys in the table pair table point to the appropriate rows in the LegacyTables and LegacyShadowTables tables.

image

And the foreign key in the LegacyShadowColumnPairs table points to the approriate row in the LegacyShadowTablePairs table. As I mentioned before, each LegacyShadowTablePairs row can point to multiple LegacyShadowColumnPairs rows.

image

So for our first task, let’s get the table names and id in the first LegacyShadowTablePairs row. Later we’ll do this in a loop, but for now we’ll keep it simple by dealing with the first pair we encounter.

SELECT Top 1 @shadowTablePairId = pairs.Id, @sourceTableName = LegacyTables.Name,  @targetTableName = LegacyShadowTables.Name
FROM LegacyShadowTablePairs pairs
Inner Join LegacyTables on LegacyTables.Id = pairs.LegacyShadowTablePair_LegacyTable
Inner Join LegacyShadowTables on LegacyShadowTables.Id = pairs.LegacyShadowTablePair_LegacyShadowTable
------------- DEBUG --------------
IF @debug = 1 PRINT @DIVIDER + @CR + N'ShadowTablePairID: ' + Convert(nvarchar(5), @shadowTablePairId) + @CR +
N'SourceTable: ' + @sourceTableName + @CR +
N'TargetTable: ' + @targetTableName + @LINE_DIVIDER
-----------------------------------

Resulting in:

image

Now get the column names:

DECLARE @ColumnNames AS Table
(
	ID int Primary Key Identity(1,1),
	SourceColumnName nvarchar(255),
	TargetColumnName nvarchar(255)
)
-- Assume this code will be used in an inner loop and clear the table just before you insert into it.
-- Otherwise you'll regret it sooner or later.
DELETE @ColumnNames
-- Insert into the table variable from the column pair rows that point to the table pair row we just read
INSERT Into @ColumnNames (SourceColumnName, TargetColumnName)
Select LegacyTableColumn, ShadowTableColumn FROM LegacyShadowColumnPairs columnPairs
WHERE columnPairs.LegacyShadowColumnPair_LegacyShadowTablePair = @shadowTablePairId
-- Set the starting index to the lowest ID (primary key) of the table and the maximum index to the highest. Don't
-- try to use 1 as the starting value. If you use this table variable in a loop, the ID will just keep incrementing
-- even if you clear the table by deleting it.
SELECT @ColumnTableIndex = MIN(ID) FROM @ColumnNames
SELECT @MaxColumnTableIndex = MAX(ID) FROM @ColumnNames		
-- Progress through the rows
WHILE @ColumnTableIndex <= @MaxColumnTableIndex
BEGIN
	DECLARE @sourceColumn nvarchar(255);			
	DECLARE @targetColumn nvarchar(255);
	SELECT @sourceColumn = SourceColumnName,  @targetColumn = TargetColumnName
	FROM @ColumnNames 
	WHERE ID = @ColumnTableIndex
	SET @theSourceColumns = @theSourceColumns + quoteName(@sourceColumn)
	SET @theTargetColumns = @theTargetColumns + quoteName(@targetColumn)
	-- Append a comma to the list of column names if we're not on the last one
	IF (@ColumnTableIndex < @MaxColumnTableIndex)
	BEGIN
		SET @theSourceColumns = @theSourceColumns + N', ';
		SET @theTargetColumns = @theTargetColumns + N', ';
	END
	SET @ColumnTableIndex = @ColumnTableIndex + 1
END

Build the sql code and execute it:

-- Build the sql code to insert the rows		
SET @sql = N' INSERT ' + @targetTableName + @CR +
N' ( ' + @theTargetColumns + N' ) ' + @CR +
N' SELECT ' + @theSourceColumns + @CR +
@FROM_CLAUSE
------------- DEBUG --------------
IF @debug = 1 PRINT 'Insert: ' + @LINE_DIVIDER + @sql + @LINE_DIVIDER
-----------------------------------
EXEC sp_executesql @sql

Check the results:

image

Ok, that’s the end of part 1. Lots more to come,

  • Specifying the unique index so we can update existing records instead of having to clear the shadow tables every time. After all, we are going to add columns and we don’t want to lose them.
  • Creating foreign key relationships.
  • Testing the shadowed columns to see if any have actually changed. If we update blindly we’ll end up with unnecessary concurrency messages in LightSwitch, and ultimately we’re going to fire off the procedure once a minute.
  • Firing off the procedure from LightSwitch
  • Logging detail information when we fire manually and summary information when we run once a minute
  • Detecting rows in the legacy table or shadow table that don’t have corresponding rows on the other side.

For more information on Dynamic SQL I recommend this article by Erland Sommarskog

Here’s the complete stored procedure up to now:

USE [LS_Malls3]
GO
/****** Object:  StoredProcedure [dbo].[uspDynamicCopy_ShoppingCenters2]    Script Date: 02/08/2013 19:26:26 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[uspDynamicCopy_ShoppingCenters2] 
AS
BEGIN
    SET NOCOUNT ON;
	DECLARE @CR NVARCHAR(1) = CHAR(13)
	DECLARE @DIVIDER NVARCHAR(25) = '-------------------------' 
	DECLARE @LINE_DIVIDER NVARCHAR(50) = @CR + @DIVIDER + @CR
	DECLARE @debug bit = 1
	DECLARE @sql nvarchar(MAX);
	DECLARE @shadowTablePairId int;
	DECLARE @targetTableName nvarchar(255);
	DECLARE @sourceTableName nvarchar(255);
	DECLARE @theTargetColumns nvarchar(MAX) = N'';
	DECLARE @theSourceColumns nvarchar(MAX) = N'';
	DECLARE @ColumnTableIndex int;
	DECLARE @MaxColumnTableIndex int;
	DECLARE @FROM_CLAUSE nvarchar(MAX) = N' FROM [ShoppingCenterMgmt].[dbo].[shopping_centers] '
    
	BEGIN TRY
		DELETE Malls
		
		SELECT Top 1 @shadowTablePairId = pairs.Id, @sourceTableName = LegacyTables.Name,  @targetTableName = LegacyShadowTables.Name
		FROM LegacyShadowTablePairs pairs
		Inner Join LegacyTables on LegacyTables.Id = pairs.LegacyShadowTablePair_LegacyTable
		Inner Join LegacyShadowTables on LegacyShadowTables.Id = pairs.LegacyShadowTablePair_LegacyShadowTable
		------------- DEBUG --------------
		IF @debug = 1 PRINT @DIVIDER + @CR + N'ShadowTablePairID: ' + Convert(nvarchar(5), @shadowTablePairId) + @CR +
		N'SourceTable: ' + @sourceTableName + @CR +
		N'TargetTable: ' + @targetTableName + @LINE_DIVIDER
		-----------------------------------
		-- Save the column names in a table variable
		DECLARE @ColumnNames AS Table
		(
			ID int Primary Key Identity(1,1),
			SourceColumnName nvarchar(255),
			TargetColumnName nvarchar(255)
		)
		-- Assume this code will be used in an inner loop and clear the table just before you insert into it.
		-- Otherwise you'll regret it sooner or later.
		DELETE @ColumnNames
		-- Insert into the table variable from the column pair rows that point to the table pair row we just read
		INSERT Into @ColumnNames (SourceColumnName, TargetColumnName)
		Select LegacyTableColumn, ShadowTableColumn FROM LegacyShadowColumnPairs columnPairs
		WHERE columnPairs.LegacyShadowColumnPair_LegacyShadowTablePair = @shadowTablePairId
		-- Set the starting index to the lowest ID (primary key) of the table and the maximum index to the highest. Don't
		-- try to use 1 as the starting value. If you use this table variable in a loop, the ID will just keep incrementing
		-- even if you clear the table by deleting it.
		SELECT @ColumnTableIndex = MIN(ID) FROM @ColumnNames
		SELECT @MaxColumnTableIndex = MAX(ID) FROM @ColumnNames		
		-- Progress through the rows
		WHILE @ColumnTableIndex <= @MaxColumnTableIndex
		BEGIN
			DECLARE @sourceColumn nvarchar(255);			
			DECLARE @targetColumn nvarchar(255);
			SELECT @sourceColumn = SourceColumnName,  @targetColumn = TargetColumnName
			FROM @ColumnNames 
			WHERE ID = @ColumnTableIndex
			SET @theSourceColumns = @theSourceColumns + quoteName(@sourceColumn)
			SET @theTargetColumns = @theTargetColumns + quoteName(@targetColumn)
			-- Append a comma to the list of column names if we're not on the last one
			IF (@ColumnTableIndex < @MaxColumnTableIndex)
			BEGIN
				SET @theSourceColumns = @theSourceColumns + N', ';
				SET @theTargetColumns = @theTargetColumns + N', ';
			END
			SET @ColumnTableIndex = @ColumnTableIndex + 1
		END
		SELECT @theSourceColumns
		SELECT @theTargetColumns
		
		-- Build the sql code to insert the rows		
		SET @sql = N' INSERT ' + @targetTableName + @CR +
		N' ( ' + @theTargetColumns + N' ) ' + @CR +
		N' SELECT ' + @theSourceColumns + @CR +
		@FROM_CLAUSE
		------------- DEBUG --------------
		IF @debug = 1 PRINT 'Insert: ' + @LINE_DIVIDER + @sql + @LINE_DIVIDER
		-----------------------------------
		EXEC sp_executesql @sql
		
    END TRY
	BEGIN CATCH
        EXECUTE [dbo].[uspPrintError];
        -- Rollback any active or uncommittable transactions before
        -- inserting information in the ErrorLog
        IF @@TRANCOUNT > 0
        BEGIN
            ROLLBACK TRANSACTION;
        END
        EXECUTE [dbo].[uspLogProcError];
        RETURN -1;
	END CATCH
END;

 

6 comment(s) so far...


Gravatar

Re: Rescue Legacy Data using LightSwitch, Stored Procedures, and Dynamic SQL, Part 1

Could you have solved your problem with a View?

By Bill Ross on   2/16/2013 9:34 PM
Gravatar

Re: Rescue Legacy Data using LightSwitch, Stored Procedures, and Dynamic SQL, Part 1

wow, how big and how great job, Richard.

thanks about your contribution with our lightswitch projects.

By Mauricio on   2/16/2013 9:34 PM
Gravatar

Re: Rescue Legacy Data using LightSwitch, Stored Procedures, and Dynamic SQL, Part 1

Bill: No

By Richard Waddell on   2/16/2013 9:34 PM
Gravatar

Re: Rescue Legacy Data using LightSwitch, Stored Procedures, and Dynamic SQL, Part 1

Mauricio: Thanks. This was my first attempt at Dynamic SQL - many thanks to Erland Sommarskog whose article made it possible.

By Richard Waddell on   2/16/2013 9:37 PM
Gravatar

Re: Rescue Legacy Data using LightSwitch, Stored Procedures, and Dynamic SQL, Part 1

How soon will Part 2, featuring the perennial roadblock I encounter, "Firing off the procedure from LightSwitch" (successfully), be published, please, Richard?

We have tons of SP's and new "command-style" SPs that we're itching to let loose via LightSwitch but have yet to successfully find a reliable way of firing them off. We haven't time, and aren't daft enough, to replicate the data rules into LS from the working SPs. Old UI is Flex/Flash and we desperately need to get off it into HTML5, hence LS.

Hope your next installment covers this...

Cheers!

By Stephen on   2/21/2013 9:10 PM
Gravatar

Re: Rescue Legacy Data using LightSwitch, Stored Procedures, and Dynamic SQL, Part 1

Stephen: Is your problem specifically with launching the stored procedure. If so I'll try to get you something before the next post, which I'm going to try to start in the next couple of days. So let me know if having just that code would be helpful.
Regards,
Richard

By Richard Waddell on   2/21/2013 9:12 PM

Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Security Code
CAPTCHA image
Enter the code shown above in the box below
Add Comment   Cancel 
Microsoft Visual Studio is a registered trademark of Microsoft Corporation / LightSwitch is a registered trademark of Microsoft Corporation