Sunday, July 19, 2026

Replicate the local db

 In this article, we will discuss about replicate the local db






BacpacReplicaService.cs

-------------------------------------

using Microsoft.SqlServer.Dac;


namespace QaLocalDbReplica;


public sealed class BacpacReplicaService

{

    public event EventHandler<string>? StatusChanged;

    public event EventHandler<int>? ProgressChanged;


    public async Task<string> CreateReplicaAsync(

        ReplicaRequest request,

        CancellationToken cancellationToken)

    {

        ValidateRequest(request);


        await LocalDbService.EnsureLocalDbAvailableAsync(cancellationToken);


        string tempDirectory = Path.Combine(

            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),

            "QaLocalDbReplica",

            "Bacpac");


        Directory.CreateDirectory(tempDirectory);


        string bacpacPath = Path.Combine(

            tempDirectory,

            $"{SanitizeFileName(request.SourceDatabase)}_{DateTime.Now:yyyyMMdd_HHmmss}.bacpac");


        string sourceConnectionString =

            ConnectionStringFactory.BuildSource(

                request.SourceServer,

                request.SourceDatabase,

                request.UseWindowsAuthentication,

                request.UserName,

                request.Password);


        try

        {

            ReportStatus("Exporting the selected QA database to BACPAC...");

            ReportProgress(0);


            await Task.Run(

                () => ExportBacpac(

                    sourceConnectionString,

                    request.SourceDatabase,

                    bacpacPath,

                    cancellationToken),

                cancellationToken);


            ReportProgress(50);


            bool exists = await LocalDbService.DatabaseExistsAsync(

                request.DestinationDatabase,

                cancellationToken);


            if (exists && !request.ReplaceExistingDestination)

            {

                throw new InvalidOperationException(

                    $"LocalDB database '{request.DestinationDatabase}' already exists.");

            }


            if (exists)

            {

                ReportStatus(

                    $"Removing existing LocalDB database '{request.DestinationDatabase}'...");


                await LocalDbService.DropDatabaseIfExistsAsync(

                    request.DestinationDatabase,

                    cancellationToken);

            }


            ReportStatus("Importing BACPAC into LocalDB...");


            await Task.Run(

                () => ImportBacpac(

                    request.DestinationDatabase,

                    bacpacPath,

                    cancellationToken),

                cancellationToken);


            ReportProgress(100);

            ReportStatus(

                $"Replica '{request.DestinationDatabase}' was created successfully.");


            return bacpacPath;

        }

        finally

        {

            if (request.DeleteTemporaryBacpac)

            {

                TryDeleteFile(bacpacPath);

            }

        }

    }


    private void ExportBacpac(

        string sourceConnectionString,

        string sourceDatabase,

        string bacpacPath,

        CancellationToken cancellationToken)

    {

        if (File.Exists(bacpacPath))

            File.Delete(bacpacPath);


        // Try to fix permissions issue proactively

        TryFixAzureSqlPermissions(sourceConnectionString);


        var services = new DacServices(sourceConnectionString);


        services.Message += (_, e) =>

            ReportStatus($"Export: {e.Message.Message}");


        services.ProgressChanged += (_, e) =>

        {

            int mapped = Math.Clamp((int)e.Status / 2, 0, 50);

            ReportProgress(mapped);


            if (!string.IsNullOrWhiteSpace(e.Message))

                ReportStatus($"Export: {e.Message}");

        };


        var options = new DacExportOptions

        {

            CommandTimeout = 0,

            VerifyExtraction = false,

            Storage = DacSchemaModelStorageType.Memory

        };


        services.ExportBacpac(

            bacpacPath,

            sourceDatabase,

            options,

            tables: null,

            cancellationToken);

    }


    private void TryFixAzureSqlPermissions(string connectionString)

    {

        try

        {

            using var connection = new Microsoft.Data.SqlClient.SqlConnection(connectionString);

            connection.Open();


            ReportStatus("Checking database compatibility settings...");


            // Try to revoke the problematic CONNECT permission from guest user

            try

            {

                string fixSql = "REVOKE CONNECT FROM guest;";

                using var command = new Microsoft.Data.SqlClient.SqlCommand(fixSql, connection);

                command.CommandTimeout = 10;

                command.ExecuteNonQuery();

                ReportStatus("Revoked CONNECT permission from guest user.");

            }

            catch (Exception ex)

            {

                ReportStatus($"Could not revoke guest CONNECT permission: {ex.Message}");

            }

        }

        catch (Exception ex)

        {

            ReportStatus($"Warning: Permission check failed: {ex.Message}");

        }

    }


    private void ImportBacpac(

        string destinationDatabase,

        string bacpacPath,

        CancellationToken cancellationToken)

    {

        var services =

            new DacServices(ConnectionStringFactory.BuildLocalMaster());


        services.Message += (_, e) =>

            ReportStatus($"Import: {e.Message.Message}");


        services.ProgressChanged += (_, e) =>

        {

            int mapped = 50 + Math.Clamp((int)e.Status / 2, 0, 50);

            ReportProgress(mapped);


            if (!string.IsNullOrWhiteSpace(e.Message))

                ReportStatus($"Import: {e.Message}");

        };


        using BacPackage package = BacPackage.Load(bacpacPath);


        var options = new DacImportOptions

        {

            CommandTimeout = 0

        };


        services.ImportBacpac(

            package,

            destinationDatabase,

            options,

            cancellationToken);

    }


    private static void ValidateRequest(ReplicaRequest request)

    {

        if (string.IsNullOrWhiteSpace(request.SourceServer))

            throw new ArgumentException("Source server is required.");


        if (string.IsNullOrWhiteSpace(request.SourceDatabase))

            throw new ArgumentException("Source database is required.");


        LocalDbService.ValidateDatabaseName(request.DestinationDatabase);


        if (!request.UseWindowsAuthentication)

        {

            if (string.IsNullOrWhiteSpace(request.UserName))

                throw new ArgumentException("SQL username is required.");


            if (string.IsNullOrWhiteSpace(request.Password))

                throw new ArgumentException("SQL password is required.");

        }

    }


    private static string SanitizeFileName(string value)

    {

        foreach (char invalid in Path.GetInvalidFileNameChars())

            value = value.Replace(invalid, '_');


        return value;

    }


    private static void TryDeleteFile(string path)

    {

        try

        {

            if (File.Exists(path))

                File.Delete(path);

        }

        catch

        {

            // Cleanup failure should not hide the real export/import result.

        }

    }


    private void ReportStatus(string message) =>

        StatusChanged?.Invoke(this, message);


    private void ReportProgress(int percentage) =>

        ProgressChanged?.Invoke(this, Math.Clamp(percentage, 0, 100));

}




-----------------------------------------------------------------------------

ConnectionStringFactory.cs
------------------------------------
using Microsoft.Data.SqlClient;

namespace QaLocalDbReplica;

public static class ConnectionStringFactory
{
    public const string LocalDbInstance = @"(localdb)\MSSQLLocalDB";

    public static string BuildSource(
        string server,
        string database,
        bool windowsAuthentication,
        string? userName,
        string? password)
    {
        var builder = new SqlConnectionStringBuilder
        {
            DataSource = server.Trim(),
            InitialCatalog = database,
            IntegratedSecurity = windowsAuthentication,
            Encrypt = false,
            TrustServerCertificate = true,
            ConnectTimeout = 30,
            ApplicationName = "QA LocalDB Replica"
        };

        if (!windowsAuthentication)
        {
            builder.UserID = userName?.Trim() ?? string.Empty;
            builder.Password = password ?? string.Empty;
        }

        return builder.ConnectionString;
    }

    public static string BuildSourceMaster(
        string server,
        bool windowsAuthentication,
        string? userName,
        string? password) =>
        BuildSource(server, "master", windowsAuthentication, userName, password);

    public static string BuildLocalMaster()
    {
        return new SqlConnectionStringBuilder
        {
            DataSource = LocalDbInstance,
            InitialCatalog = "master",
            IntegratedSecurity = true,
            Encrypt = false,
            TrustServerCertificate = true,
            ConnectTimeout = 30,
            ApplicationName = "QA LocalDB Replica"
        }.ConnectionString;
    }

    public static string BuildLocalDatabase(string database)
    {
        return new SqlConnectionStringBuilder
        {
            DataSource = LocalDbInstance,
            InitialCatalog = database,
            IntegratedSecurity = true,
            Encrypt = false,
            TrustServerCertificate = true,
            ConnectTimeout = 30,
            ApplicationName = "QA LocalDB Re.csplica"
        }.ConnectionString;
    }
}


-------------------------------------------------------------------------------------------

DatabaseListItem.cs
---------------------------------
namespace QaLocalDbReplica;

public sealed class DatabaseListItem
{
    public string Name { get; init; } = string.Empty;

    public override string ToString() => Name;
}


----------------------------------------------------------------------------------------------------
LocalDbService.cs
----------------------------
using Microsoft.Data.SqlClient;
using System.Text.RegularExpressions;

namespace QaLocalDbReplica;

public static class LocalDbService
{
    private static readonly Regex ValidDatabaseName =
        new(@"^[A-Za-z0-9_]+$", RegexOptions.Compiled);

    public static void ValidateDatabaseName(string databaseName)
    {
        if (string.IsNullOrWhiteSpace(databaseName))
            throw new ArgumentException("Destination database name is required.");

        if (!ValidDatabaseName.IsMatch(databaseName))
        {
            throw new ArgumentException(
                "Destination database name can contain only letters, numbers, and underscore.");
        }
    }

    public static async Task EnsureLocalDbAvailableAsync(
        CancellationToken cancellationToken)
    {
        await using var connection =
            new SqlConnection(ConnectionStringFactory.BuildLocalMaster());

        await connection.OpenAsync(cancellationToken);

        await using var command =
            new SqlCommand("SELECT CAST(SERVERPROPERTY('ProductVersion') AS nvarchar(50));", connection);

        _ = await command.ExecuteScalarAsync(cancellationToken);
    }

    public static async Task<bool> DatabaseExistsAsync(
        string databaseName,
        CancellationToken cancellationToken)
    {
        await using var connection =
            new SqlConnection(ConnectionStringFactory.BuildLocalMaster());

        await connection.OpenAsync(cancellationToken);

        await using var command =
            new SqlCommand("SELECT CASE WHEN DB_ID(@name) IS NULL THEN 0 ELSE 1 END;", connection);

        command.Parameters.AddWithValue("@name", databaseName);

        return Convert.ToInt32(
            await command.ExecuteScalarAsync(cancellationToken)) == 1;
    }

    public static async Task DropDatabaseIfExistsAsync(
        string databaseName,
        CancellationToken cancellationToken)
    {
        ValidateDatabaseName(databaseName);

        string quotedName = QuoteIdentifier(databaseName);

        string sql = $"""
IF DB_ID(@databaseName) IS NOT NULL
BEGIN
    ALTER DATABASE {quotedName}
        SET SINGLE_USER
        WITH ROLLBACK IMMEDIATE;

    DROP DATABASE {quotedName};
END;
""";

        await using var connection =
            new SqlConnection(ConnectionStringFactory.BuildLocalMaster());

        await connection.OpenAsync(cancellationToken);

        await using var command = new SqlCommand(sql, connection)
        {
            CommandTimeout = 0
        };

        command.Parameters.AddWithValue("@databaseName", databaseName);

        await command.ExecuteNonQueryAsync(cancellationToken);
    }

    private static string QuoteIdentifier(string value) =>
        "[" + value.Replace("]", "]]") + "]";
}



----------------------------------------------------------------------------------------------------
MainForm.cs
-------------------------
namespace QaLocalDbReplica;

public partial class MainForm : Form
{
    private CancellationTokenSource? _operationCancellation;

    public MainForm()
    {
        InitializeComponent();

        cmbAuthentication.SelectedIndex = 0;
        txtDestinationDatabase.Text = "QA_Local";
        UpdateAuthenticationControls();
    }

    private async void btnLoadDatabases_Click(object sender, EventArgs e)
    {
        try
        {
            SetBusy(true);
            ClearLog();

            ValidateSourceInput(requireDatabase: false);

            Log("Connecting to source SQL Server...");
            lblStatus.Text = "Loading source databases...";

            using var cancellation = new CancellationTokenSource();

            IReadOnlyList<DatabaseListItem> databases =
                await SourceDatabaseService.GetDatabasesAsync(
                    txtSourceServer.Text.Trim(),
                    UseWindowsAuthentication,
                    txtUserName.Text.Trim(),
                    txtPassword.Text,
                    cancellation.Token);

            cmbSourceDatabase.DataSource = databases.ToList();

            Log($"Loaded {databases.Count} database(s).");
            lblStatus.Text = "Select the QA database.";
        }
        catch (Exception ex)
        {
            ShowError("Unable to load databases", ex);
        }
        finally
        {
            SetBusy(false);
        }
    }

    private async void btnSubmit_Click(object sender, EventArgs e)
    {
        try
        {
            ValidateSourceInput(requireDatabase: true);
            LocalDbService.ValidateDatabaseName(txtDestinationDatabase.Text.Trim());

            SetBusy(true);
            btnCancel.Enabled = true;
            ClearLog();
            progressBar.Value = 0;

            _operationCancellation = new CancellationTokenSource();

            var request = new ReplicaRequest
            {
                SourceServer = txtSourceServer.Text.Trim(),
                SourceDatabase =
                    ((DatabaseListItem)cmbSourceDatabase.SelectedItem!).Name,
                DestinationDatabase = txtDestinationDatabase.Text.Trim(),
                UseWindowsAuthentication = UseWindowsAuthentication,
                UserName = txtUserName.Text.Trim(),
                Password = txtPassword.Text,
                ReplaceExistingDestination = chkReplaceExisting.Checked,
                DeleteTemporaryBacpac = chkDeleteBacpac.Checked
            };

            var service = new BacpacReplicaService();

            service.StatusChanged += (_, message) =>
                SafeUi(() =>
                {
                    lblStatus.Text = message;
                    Log(message);
                });

            service.ProgressChanged += (_, percentage) =>
                SafeUi(() => progressBar.Value = percentage);

            string bacpacPath =
                await service.CreateReplicaAsync(
                    request,
                    _operationCancellation.Token);

            string localConnection =
                ConnectionStringFactory.BuildLocalDatabase(
                    request.DestinationDatabase);

            string message = request.DeleteTemporaryBacpac
                ? $"LocalDB replica '{request.DestinationDatabase}' was created successfully."
                : $"LocalDB replica was created successfully.\n\nBACPAC: {bacpacPath}";

            MessageBox.Show(
                message +
                $"\n\nConnection string:\n{localConnection}",
                "Completed",
                MessageBoxButtons.OK,
                MessageBoxIcon.Information);
        }
        catch (OperationCanceledException)
        {
            lblStatus.Text = "Operation cancelled.";
            Log("Operation cancelled.");
        }
        catch (Exception ex)
        {
            ShowError("Replica creation failed", ex);
        }
        finally
        {
            _operationCancellation?.Dispose();
            _operationCancellation = null;

            btnCancel.Enabled = false;
            SetBusy(false);
        }
    }

    private void btnCancel_Click(object sender, EventArgs e)
    {
        btnCancel.Enabled = false;
        lblStatus.Text = "Cancellation requested...";
        Log("Cancellation requested...");
        _operationCancellation?.Cancel();
    }

    private void cmbAuthentication_SelectedIndexChanged(
        object sender,
        EventArgs e)
    {
        UpdateAuthenticationControls();
    }

    private bool UseWindowsAuthentication =>
        cmbAuthentication.SelectedIndex == 0;

    private void UpdateAuthenticationControls()
    {
        bool sqlAuthentication = !UseWindowsAuthentication;

        txtUserName.Enabled = sqlAuthentication;
        txtPassword.Enabled = sqlAuthentication;
    }

    private void ValidateSourceInput(bool requireDatabase)
    {
        if (string.IsNullOrWhiteSpace(txtSourceServer.Text))
            throw new InvalidOperationException("Enter the source SQL Server name.");

        if (!UseWindowsAuthentication)
        {
            if (string.IsNullOrWhiteSpace(txtUserName.Text))
                throw new InvalidOperationException("Enter the SQL username.");

            if (string.IsNullOrWhiteSpace(txtPassword.Text))
                throw new InvalidOperationException("Enter the SQL password.");
        }

        if (requireDatabase && cmbSourceDatabase.SelectedItem is null)
            throw new InvalidOperationException("Select the source QA database.");
    }

    private void SetBusy(bool busy)
    {
        btnLoadDatabases.Enabled = !busy;
        btnSubmit.Enabled = !busy;
        txtSourceServer.Enabled = !busy;
        cmbAuthentication.Enabled = !busy;
        cmbSourceDatabase.Enabled = !busy;
        txtDestinationDatabase.Enabled = !busy;
        chkReplaceExisting.Enabled = !busy;
        chkDeleteBacpac.Enabled = !busy;

        if (!busy)
            UpdateAuthenticationControls();
        else
        {
            txtUserName.Enabled = false;
            txtPassword.Enabled = false;
        }

        UseWaitCursor = busy;
    }

    private void Log(string message)
    {
        txtLog.AppendText(
            $"{DateTime.Now:HH:mm:ss}  {message}{Environment.NewLine}");
    }

    private void ClearLog()
    {
        txtLog.Clear();
        lblStatus.Text = "Ready";
        progressBar.Value = 0;
    }

    private void ShowError(string title, Exception exception)
    {
        lblStatus.Text = title;
        Log(exception.ToString());

        MessageBox.Show(
            exception.Message,
            title,
            MessageBoxButtons.OK,
            MessageBoxIcon.Error);
    }

    private void SafeUi(Action action)
    {
        if (IsDisposed)
            return;

        if (InvokeRequired)
            BeginInvoke(action);
        else
            action();
    }
}


-----------------------------------------------------------------------
MainForm.Designer.cs
-------------------------------------
namespace QaLocalDbReplica;

partial class MainForm
{
    private System.ComponentModel.IContainer? components = null;

    private Label lblSourceServer;
    private TextBox txtSourceServer;
    private Label lblAuthentication;
    private ComboBox cmbAuthentication;
    private Label lblUserName;
    private TextBox txtUserName;
    private Label lblPassword;
    private TextBox txtPassword;
    private Button btnLoadDatabases;
    private Label lblSourceDatabase;
    private ComboBox cmbSourceDatabase;
    private Label lblDestinationDatabase;
    private TextBox txtDestinationDatabase;
    private CheckBox chkReplaceExisting;
    private CheckBox chkDeleteBacpac;
    private Button btnSubmit;
    private Button btnCancel;
    private ProgressBar progressBar;
    private Label lblStatus;
    private TextBox txtLog;

    protected override void Dispose(bool disposing)
    {
        if (disposing)
            components?.Dispose();

        base.Dispose(disposing);
    }

    private void InitializeComponent()
    {
        lblSourceServer = new Label();
        txtSourceServer = new TextBox();
        lblAuthentication = new Label();
        cmbAuthentication = new ComboBox();
        lblUserName = new Label();
        txtUserName = new TextBox();
        lblPassword = new Label();
        txtPassword = new TextBox();
        btnLoadDatabases = new Button();
        lblSourceDatabase = new Label();
        cmbSourceDatabase = new ComboBox();
        lblDestinationDatabase = new Label();
        txtDestinationDatabase = new TextBox();
        chkReplaceExisting = new CheckBox();
        chkDeleteBacpac = new CheckBox();
        btnSubmit = new Button();
        btnCancel = new Button();
        progressBar = new ProgressBar();
        lblStatus = new Label();
        txtLog = new TextBox();
        SuspendLayout();

        lblSourceServer.AutoSize = true;
        lblSourceServer.Location = new Point(24, 25);
        lblSourceServer.Text = "Source SQL Server";

        txtSourceServer.Location = new Point(190, 21);
        txtSourceServer.Size = new Size(420, 27);
        txtSourceServer.PlaceholderText = @"Example: QA-SERVER or QA-SERVER\INSTANCE";

        lblAuthentication.AutoSize = true;
        lblAuthentication.Location = new Point(24, 66);
        lblAuthentication.Text = "Authentication";

        cmbAuthentication.DropDownStyle = ComboBoxStyle.DropDownList;
        cmbAuthentication.Items.AddRange(
            ["Windows Authentication", "SQL Server Authentication"]);
        cmbAuthentication.Location = new Point(190, 62);
        cmbAuthentication.Size = new Size(260, 28);
        cmbAuthentication.SelectedIndexChanged +=
            cmbAuthentication_SelectedIndexChanged;

        lblUserName.AutoSize = true;
        lblUserName.Location = new Point(24, 107);
        lblUserName.Text = "SQL username";

        txtUserName.Location = new Point(190, 103);
        txtUserName.Size = new Size(260, 27);

        lblPassword.AutoSize = true;
        lblPassword.Location = new Point(24, 148);
        lblPassword.Text = "SQL password";

        txtPassword.Location = new Point(190, 144);
        txtPassword.Size = new Size(260, 27);
        txtPassword.UseSystemPasswordChar = true;

        btnLoadDatabases.Location = new Point(470, 102);
        btnLoadDatabases.Size = new Size(140, 69);
        btnLoadDatabases.Text = "Load Databases";
        btnLoadDatabases.UseVisualStyleBackColor = true;
        btnLoadDatabases.Click += btnLoadDatabases_Click;

        lblSourceDatabase.AutoSize = true;
        lblSourceDatabase.Location = new Point(24, 192);
        lblSourceDatabase.Text = "Source database";

        cmbSourceDatabase.DropDownStyle = ComboBoxStyle.DropDownList;
        cmbSourceDatabase.Location = new Point(190, 188);
        cmbSourceDatabase.Size = new Size(420, 28);

        lblDestinationDatabase.AutoSize = true;
        lblDestinationDatabase.Location = new Point(24, 233);
        lblDestinationDatabase.Text = "LocalDB database";

        txtDestinationDatabase.Location = new Point(190, 229);
        txtDestinationDatabase.Size = new Size(420, 27);

        chkReplaceExisting.AutoSize = true;
        chkReplaceExisting.Checked = true;
        chkReplaceExisting.CheckState = CheckState.Checked;
        chkReplaceExisting.Location = new Point(190, 273);
        chkReplaceExisting.Text = "Replace destination database when it already exists";

        chkDeleteBacpac.AutoSize = true;
        chkDeleteBacpac.Checked = true;
        chkDeleteBacpac.CheckState = CheckState.Checked;
        chkDeleteBacpac.Location = new Point(190, 307);
        chkDeleteBacpac.Text = "Delete temporary BACPAC after successful/failed operation";

        btnSubmit.Location = new Point(190, 350);
        btnSubmit.Size = new Size(160, 42);
        btnSubmit.Text = "Create Replica";
        btnSubmit.UseVisualStyleBackColor = true;
        btnSubmit.Click += btnSubmit_Click;

        btnCancel.Enabled = false;
        btnCancel.Location = new Point(370, 350);
        btnCancel.Size = new Size(120, 42);
        btnCancel.Text = "Cancel";
        btnCancel.UseVisualStyleBackColor = true;
        btnCancel.Click += btnCancel_Click;

        progressBar.Location = new Point(24, 414);
        progressBar.Size = new Size(586, 25);

        lblStatus.AutoEllipsis = true;
        lblStatus.Location = new Point(24, 450);
        lblStatus.Size = new Size(586, 42);
        lblStatus.Text = "Ready";

        txtLog.Location = new Point(24, 500);
        txtLog.Multiline = true;
        txtLog.ReadOnly = true;
        txtLog.ScrollBars = ScrollBars.Both;
        txtLog.Size = new Size(586, 180);
        txtLog.WordWrap = false;

        AutoScaleDimensions = new SizeF(8F, 20F);
        AutoScaleMode = AutoScaleMode.Font;
        ClientSize = new Size(638, 704);
        Controls.Add(txtLog);
        Controls.Add(lblStatus);
        Controls.Add(progressBar);
        Controls.Add(btnCancel);
        Controls.Add(btnSubmit);
        Controls.Add(chkDeleteBacpac);
        Controls.Add(chkReplaceExisting);
        Controls.Add(txtDestinationDatabase);
        Controls.Add(lblDestinationDatabase);
        Controls.Add(cmbSourceDatabase);
        Controls.Add(lblSourceDatabase);
        Controls.Add(btnLoadDatabases);
        Controls.Add(txtPassword);
        Controls.Add(lblPassword);
        Controls.Add(txtUserName);
        Controls.Add(lblUserName);
        Controls.Add(cmbAuthentication);
        Controls.Add(lblAuthentication);
        Controls.Add(txtSourceServer);
        Controls.Add(lblSourceServer);
        FormBorderStyle = FormBorderStyle.FixedDialog;
        MaximizeBox = false;
        StartPosition = FormStartPosition.CenterScreen;
        Text = "QA to LocalDB Replica";
        ResumeLayout(false);
        PerformLayout();
    }
}


--------------------------------------------------------------------------------------------------------
Program.cs
-----------------------
namespace QaLocalDbReplica
{
    internal static class Program
    {
        /// <summary>
        ///  The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            // To customize application configuration such as set high DPI settings or default font,
            // see https://aka.ms/applicationconfiguration.
            ApplicationConfiguration.Initialize();
            Application.Run(new MainForm());
        }
    }
}


----------------------------------------------------------------------------------------------------------------------
ReplicaRequest.cs
----------------------------
namespace QaLocalDbReplica;

public sealed class ReplicaRequest
{
    public required string SourceServer { get; init; }
    public required string SourceDatabase { get; init; }
    public required string DestinationDatabase { get; init; }

    public bool UseWindowsAuthentication { get; init; }
    public string? UserName { get; init; }
    public string? Password { get; init; }

    public bool ReplaceExistingDestination { get; init; } = true;
    public bool DeleteTemporaryBacpac { get; init; } = true;
}


-----------------------------------------------------------------------------------------------------------------
SourceDatabaseService.cs
--------------------------------------

using Microsoft.Data.SqlClient;

namespace QaLocalDbReplica;

public static class SourceDatabaseService
{
    public static async Task<IReadOnlyList<DatabaseListItem>> GetDatabasesAsync(
        string server,
        bool windowsAuthentication,
        string? userName,
        string? password,
        CancellationToken cancellationToken)
    {
        string connectionString =
            ConnectionStringFactory.BuildSourceMaster(
                server,
                windowsAuthentication,
                userName,
                password);

        const string sql = """
SELECT name
FROM sys.databases
ORDER BY name;
""";

        var result = new List<DatabaseListItem>();

        await using var connection = new SqlConnection(connectionString);
        await connection.OpenAsync(cancellationToken);

        await using var command = new SqlCommand(sql, connection)
        {
            CommandTimeout = 60
        };

        await using var reader =
            await command.ExecuteReaderAsync(cancellationToken);

        while (await reader.ReadAsync(cancellationToken))
        {
            result.Add(new DatabaseListItem
            {
                Name = reader.GetString(0)
            });
        }

        return result;
    }
}
















0 comments:

Post a Comment