4

We created an "on-prem" web app and I have been tasked with creating an installer for the app that will programmatically allow the user to choose between a SQLite or SQL Server implementation. I have zero clue on how to do this and have not found any good articles with clear direction.

All I have done is write the following code in my Startup.cs file to choose between two connection strings located in my appsettings.json file. Does anyone know the best way to create/implement an installer? Are there open source solutions for this kind of thing? I feel so lost on this one....

protected virtual IServiceCollection ConfigureDbContext(IServiceCollection services)
        {
            var isSqlServerConnection = Configuration.GetValue<bool>("UseSql");

            if (isSqlServerConnection)
            {
                services.AddDbContext<SecurityDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("Default")).UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll),
                ServiceLifetime.Transient);

                services.AddDbContext<StorageContext>(options =>
                options.UseSqlite(Configuration.GetConnectionString("Default")).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking),
                ServiceLifetime.Transient);
            }
            else
            {
                services.AddDbContext<SecurityDbContext>(options =>
                options.UseSqlite(Configuration.GetConnectionString("Sqlite")).UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll),
                ServiceLifetime.Transient);

                services.AddDbContext<StorageContext>(options =>
                options.UseSqlite(Configuration.GetConnectionString("Sqlite")).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
             , ServiceLifetime.Transient);
            }

            return services;
        }
Preben Huybrechts
  • 5,853
  • 2
  • 27
  • 63
Dean Friedland
  • 773
  • 2
  • 12
  • 32
  • 2
    What did you look at so far? There are some open source solutions out there, e.g. [WiX toolset](https://wixtoolset.org/), [NSIS](https://nsis.sourceforge.io/Main_Page), ... You should also be able to [boostrap SQL Server](https://stackoverflow.com/a/19010097) with the installer – kapsiR Aug 17 '20 at 21:48
  • Assuming this is a SASS product you want to distribute, have you considered creating 2 docker containers one for SQLite and one fore SQL Server? And distributed it via docker? – Preben Huybrechts Aug 19 '20 at 04:46

2 Answers2

2

Create an installer with Visual Studio

  1. Close all but one instance of Visual Studio.
  2. In the running instance, access the menu Tools->Extensions and Updates.
  3. In that dialog, choose Online->Visual Studio Marketplace->Tools->Setup & Deployment.
  4. From the list that appears, select Microsoft Visual Studio 2017 Installer Projects.
  5. Once installed, close and restart Visual Studio. Go to File->New Project and search for the word Installer. You'll know you have the correct templates installed if you see a list that looks something like this:

vs example

  1. Create an installer using a Setup Project to suite your needs. You can easily create a page on the installer where the user chooses SQLite or SQL Server as the data backed for example.

Here are some additional resources on creating an installer and the extension you'd need. You may need another version of the extension depending on your Version of Visual Studio.

https://marketplace.visualstudio.com/items?itemName=VisualStudioClient.MicrosoftVisualStudio2017InstallerProjects

https://codeteddy.com/2016/04/04/creating-an-msi-package-for-c-windows-application-using-a-visual-studio-setup-project/

https://www.add-in-express.com/docs/net-msi-setup-project.php

vvvv4d
  • 3,881
  • 1
  • 14
  • 18
  • 1
    Ok. So I think my biggest question then is regarding when the user selects the database engine during installation. Will the installer just replace the value of the `UseSql` key in my appsettings.json file based on the choice made by the user? – Dean Friedland Aug 18 '20 at 13:26
  • 1
    Yes, you could write method(s) in the setup project for that exact purpose. There isn't going to be something "out of the box" and you are going to have to write code to create the functionality you need to suite your specific installer & setup requirements such as that change to appsettings.json. – vvvv4d Aug 18 '20 at 15:09
  • @Dean Friedland you need any more info? – vvvv4d Aug 22 '20 at 03:13
  • 1
    Sorry. Just got back from vacation. One other question. I am selecting your answer as correct but I am going to go the Wix route unless you think your idea is better. Is there a reason your way is better than using the Wix Toolset? I was looking at this tutorial... https://www.youtube.com/watch?v=6Yf-eDsRrnM – Dean Friedland Aug 24 '20 at 14:42
  • Wix Toolset looks like an even better approach! I'm not very familiar with it but I did find a good article on Microsoft: Check out this article and let me know if you have any questions happy to jump on chat. https://learn.microsoft.com/en-us/archive/msdn-magazine/2007/march/automate-releases-with-msbuild-and-windows-installer-xml – vvvv4d Aug 24 '20 at 15:16
  • @vvv4d I built the installer and now need to figure out how to change the appsettings.json values. I think I need to create a custom action project, create dialog box and checkbox in installer, pass arguments from dialog box and checkbox to custom action, and custom action will modify the values in the `appsettings.json` file. Does this all sound correct? This is completely theory and have not been able to find an implementation of this.... – Dean Friedland Aug 27 '20 at 14:24
  • @deanfriedland yes, check this out https://stackoverflow.com/questions/41653688/asp-net-core-appsettings-json-update-in-code and. – vvvv4d Aug 27 '20 at 16:03
  • any chance you can look at this. I did what you said and I'm stuck on the last part. I'm almost there! So close.... https://stackoverflow.com/q/63642804/5327323 – Dean Friedland Aug 29 '20 at 13:36
  • I'll follow up Monday. We can hop on chat too. – vvvv4d Aug 29 '20 at 15:00
2

You can also use "Inno Setup" to create your installer. There are plenty of sample code out there. Basically you need to do a few things.

Preparation: Create your own tool to write info to your appSettings.json. The exe should take in parameters as arguments and generate a appSetting.json file based on those arguments.

  1. In the inno setup files section, set the path to your built artifacts, and your own json generation tool.

//Sample Code

[Files]
    Source: bin\*; DestDir: {app}\bin; Flags: recursesubdirs uninsneveruninstall; Components: Main
    Source: Utilities\AppSettingGenerator.exe; DestDir: {app}\Utilities\AppSettingGenerator.exe; Components: " Main"; Tasks: ; Flags: uninsneveruninstall; 
  1. create a screen to let user select the database engine, sqllite or Sql server.
  2. Create another screen to setup the connection strings.

//Sample Code. Create installer screens.

procedure FormCreatePage(PreviousPageId: Integer);
begin
    pgeInstallType := CreateInputOptionPage(    wpWelcome,
                                                'Select Installation Type',
                                                'Which type of installation do you want to run?',
                                                'Select the type of installation that you would like to run. Click Next when you are ready to continue.',
                                                true,
                                                false
                                             );

    pgeInstallType.Add('Sqllite');
    pgeInstallType.Add('Sql Server');

    pgeInstallType.Values[0] := true;

    
    pgeInput1 := CreateCustomPage(  PreviousPageId,
                                    'Configure Sql Server Connection',
                                    'Please verify the details for those sections highlighted in red before continuing.'
                                 );

    pgeInput2 := CreateCustomPage(  pgeInput1.ID,
                                    'Configure Sql lite Connection',
                                    'Please verify the details for those sections highlighted in red before continuing.'
                                 );
end;

//Sample code. Create UI controls to let user key in the Sql Server connection

pnlSQL := TPanel.Create(pgeInput1);
    with pnlSQL do
        begin
            Parent := pgeInput1.Surface;
            Left := ScaleX(0);
            Top := ScaleY(30);
            Width := ScaleX(413);
            Height := ScaleY(125);
            BevelInner := bvLowered;
        end;

    { lblPnlSQL }
    lblPnlSQL := TLabel.Create(pgeInput1);
    with lblPnlSQL do
        begin
            Parent := pnlSQL;
            Left := ScaleX(340);
            Top := ScaleY(5);
            Width := ScaleX(70);
            Height := ScaleY(13);
            AutoSize := False;
            Caption := 'SQL Server';
            Font.Height := ScaleY(-11);
            Font.Style := [fsBold, fsItalic];
        end;

    { lblSrvName }
    lblSrvName := TLabel.Create(pgeInput1);
    with lblSrvName do
        begin
            Parent := pnlSQL;
            Left := ScaleX(5);
            Top := ScaleY(5);
            Width := ScaleX(66);
            Height := ScaleY(13);
            Caption := 'Server Name:';
            Font.Height := ScaleY(-11);
        end;

    { lblUserID }
    lblUserID := TLabel.Create(pgeInput1);
    with lblUserID do
        begin
            Parent := pnlSQL;
            Left := ScaleX(5);
            Top := ScaleY(25);
            Width := ScaleX(40);
            Height := ScaleY(13);
            Caption := 'User ID:';
            Font.Height := ScaleY(-11);
        end;

    { lblPassword }
    lblPassword := TLabel.Create(pgeInput1);
    with lblPassword do
        begin
            Parent := pnlSQL;
            Left := ScaleX(5);
            Top := ScaleY(46);
            Width := ScaleX(50);
            Height := ScaleY(13);
            Caption := 'Password:';
            Font.Height := ScaleY(-11);
        end;

    { lblDBName }
    lblDBName := TLabel.Create(pgeInput1);
    with lblDBName do
        begin
            Parent := pnlSQL;
            Left := ScaleX(5);
            Top := ScaleY(67);
            Width := ScaleX(80);
            Height := ScaleY(13);
            Caption := 'Database Name:';
            Font.Height := ScaleY(-11);
        end;
  1. Call your appsettingGenerator tool after the parameters are entered on the installer screen.

//Sample Code. Call it at nextbuttonClick and check whether the current page is correct

function NextButtonClick(CurPageID: Integer): Boolean; 
var     i: Integer; 
begin 
  if CurPageID = pgeInput2.ID then      
  begin                                 
    RunExe(gUtilAppsettingGenerator,' -dbuid "' + txtUserID.Text + '"
     -dbpass "' + txtPassword.Text + '" -c "server=' + txtSrvName.Text + ';database='    + txtDBName.Text + '" -dbinst "' + txtDSN.Text + '"', ewWaitUntilTerminated, true); 

end;
Hainan Zhao
  • 1,962
  • 19
  • 19