Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions src/Rinsen.DatabaseInstaller/AddValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,24 @@ public class AddValue : IDbChange
{
public string TableName { get; }

public string ColumnName { get; set; }
public string ColumnName { get; set; } = string.Empty;


public AddValue(string tableName)
{
ArgumentNullException.ThrowIfNull(tableName);

TableName = tableName;
}
}

public IReadOnlyList<string> GetUpScript(InstallerOptions installerOptions)
{
return new List<string> { $"UPDATE [{installerOptions.DatabaseName}].[{installerOptions.Schema}].[{TableName}]{Environment.NewLine}SET {ColumnName} = NEWID(){Environment.NewLine}WHERE {ColumnName} is NULL" };
if (string.IsNullOrEmpty(ColumnName))
{
throw new NotSupportedException("Empty column name is not supported.");
}

return [$"UPDATE [{installerOptions.DatabaseName}].[{installerOptions.Schema}].[{TableName}]{Environment.NewLine}SET {ColumnName} = NEWID(){Environment.NewLine}WHERE {ColumnName} is NULL"];
}

public void GuidColumn(string columnName)
Expand Down
6 changes: 3 additions & 3 deletions src/Rinsen.DatabaseInstaller/AdoNetVersionStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Collections.Generic;
using Microsoft.Data.SqlClient;
using System.Threading.Tasks;
using System.Transactions;

namespace Rinsen.DatabaseInstaller
{
Expand All @@ -25,10 +24,11 @@ public async Task CreateAsync(InstallationNameAndVersion installedNameAndVersion
command.Parameters.Add(new SqlParameter("@PreviousVersion", installedNameAndVersion.PreviousVersion));
command.Parameters.Add(new SqlParameter("@StartedInstallingVersion", installedNameAndVersion.StartedInstallingVersion));

installedNameAndVersion.Id = (int)await command.ExecuteScalarAsync();
var result = await command.ExecuteScalarAsync();
installedNameAndVersion.Id = result is int id ? id : throw new InvalidOperationException("Failed to insert and retrieve identity value.");
}

public async Task<InstallationNameAndVersion> GetAsync(string name, SqlConnection connection, SqlTransaction transaction)
public async Task<InstallationNameAndVersion?> GetAsync(string name, SqlConnection connection, SqlTransaction transaction)
{
var result = default(InstallationNameAndVersion);

Expand Down
8 changes: 4 additions & 4 deletions src/Rinsen.DatabaseInstaller/Column.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ internal Column(string name, IDbType dbType)

public bool PrimaryKey { get; internal set; } = false;

public ForeignKey ForeignKey { get; internal set; } = null;
public ForeignKey? ForeignKey { get; internal set; } = null;

public Check Check { get; internal set; } = null;
public Check? Check { get; internal set; } = null;

public DefaultValue DefaultValue { get; internal set; } = null;
public DefaultValue? DefaultValue { get; internal set; } = null;

public AutoIncrement AutoIncrement { get; internal set; } = null;
public AutoIncrement? AutoIncrement { get; internal set; } = null;
}
}
42 changes: 39 additions & 3 deletions src/Rinsen.DatabaseInstaller/ColumnBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Rinsen.DatabaseInstaller
{
public class ColumnBuilder
{
public Column Column { get; }
public Column? Column { get; }

private readonly Table _table;

Expand All @@ -32,18 +32,27 @@ public ColumnBuilder ForeignKey<T>(Expression<Func<T, object>> propertyExpressio

public ColumnBuilder NotNull()
{
if (Column == null)
throw new InvalidOperationException("Column is not initialized. Use a constructor that creates a column or add a column first.");

Column.Null = false;
return this;
}

public ColumnBuilder Null()
{
if (Column == null)
throw new InvalidOperationException("Column is not initialized. Use a constructor that creates a column or add a column first.");

Column.Null = true;
return this;
}

public ColumnBuilder Unique()
{
if (Column == null)
throw new InvalidOperationException("Column is not initialized. Use a constructor that creates a column or add a column first.");

if (Column.PrimaryKey)
{
throw new InvalidOperationException("A unique constraint can not be combined with primary key");
Expand All @@ -55,6 +64,9 @@ public ColumnBuilder Unique()

public ColumnBuilder Clustered()
{
if (Column == null)
throw new InvalidOperationException("Column is not initialized. Use a constructor that creates a column or add a column first.");

if (_table.PrimaryKeyClustered)
{
throw new InvalidOperationException("A clustered column can only be added if the primary key is not clustered");
Expand All @@ -66,6 +78,9 @@ public ColumnBuilder Clustered()

public ColumnBuilder Unique(string name)
{
if (Column == null)
throw new InvalidOperationException("Column is not initialized. Use a constructor that creates a column or add a column first.");

if (Column.PrimaryKey || _table.NamedPrimaryKeys.Any(m => m.Key == Column.Name))
{
throw new InvalidOperationException("A unique constraint can not be combined with a primary key on the same column");
Expand All @@ -77,6 +92,9 @@ public ColumnBuilder Unique(string name)

public ColumnBuilder PrimaryKey()
{
if (Column == null)
throw new InvalidOperationException("Column is not initialized. Use a constructor that creates a column or add a column first.");

if (Column.Unique || _table.NamedUniques.Any(m => m.Key == Column.Name))
{
throw new InvalidOperationException("A primary key can not be combined with a unique constraint on the same column");
Expand Down Expand Up @@ -109,8 +127,11 @@ private void AddAnyExistingPrimaryKeysToNamedPrimaryKeys(string constraintName)

public ColumnBuilder PrimaryKey(string name)
{
if (Column == null)
throw new InvalidOperationException("Column is not initialized. Use a constructor that creates a column or add a column first.");

if (_table.NamedPrimaryKeys.Count > 0 &&
!_table.NamedPrimaryKeys.Keys.Contains(name))
!_table.NamedPrimaryKeys.ContainsKey(name))
{
throw new ArgumentException("Ony one named primary key can exist");
}
Expand All @@ -125,29 +146,44 @@ public ColumnBuilder PrimaryKey(string name)

public ColumnBuilder ForeignKey(string tableName)
{
if (Column == null)
throw new InvalidOperationException("Column is not initialized. Use a constructor that creates a column or add a column first.");

return ForeignKey(tableName, Column.Name);
}

public ColumnBuilder ForeignKey(string tableName, string columnName)
{
if (Column == null)
throw new InvalidOperationException("Column is not initialized. Use a constructor that creates a column or add a column first.");

Column.ForeignKey = new ForeignKey(tableName, columnName);
return this;
}

public ColumnBuilder Check()
{
if (Column == null)
throw new InvalidOperationException("Column is not initialized. Use a constructor that creates a column or add a column first.");

Column.Check = new Check();
return this;
}

public ColumnBuilder DefaultValue()
{
if (Column == null)
throw new InvalidOperationException("Column is not initialized. Use a constructor that creates a column or add a column first.");

Column.DefaultValue = new DefaultValue();
return this;
}

public ColumnBuilder AutoIncrement(int startValue = 1, int increment = 1, bool primaryKey = true)
{
if (Column == null)
throw new InvalidOperationException("Column is not initialized. Use a constructor that creates a column or add a column first.");

if (primaryKey)
{
PrimaryKey();
Expand All @@ -156,4 +192,4 @@ public ColumnBuilder AutoIncrement(int startValue = 1, int increment = 1, bool p
return this;
}
}
}
}
19 changes: 16 additions & 3 deletions src/Rinsen.DatabaseInstaller/DatabaseVersion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,34 @@ namespace Rinsen.DatabaseInstaller
{
public abstract class DatabaseVersion
{

/// <summary>
/// Initializes a new instance of the DatabaseVersion class with the specified version number. This will user the class namespace as installation name.
/// </summary>
/// <param name="version">The version number to assign to the database version. Must be a non-negative integer.</param>
protected DatabaseVersion(int version)
: this(version, null)
{

}


/// <summary>
/// Database version description
/// </summary>
/// <param name="version">Version number</param>
/// <param name="installationName">Installation name, if none specified the default will be this class namespace</param>
public DatabaseVersion(int version, string installationName = null)
public DatabaseVersion(int version, string? installationName)
{
if (string.IsNullOrEmpty(installationName))
{
InstallationName = GetType().Namespace;
InstallationName = GetType().Namespace ?? throw new ArgumentException("Installation name cannot be null or empty");
}
else
{
{
InstallationName = installationName;
}

Version = version;
}

Expand Down
2 changes: 1 addition & 1 deletion src/Rinsen.DatabaseInstaller/IVersionStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public interface IVersionStorage

Task<bool> IsInstalledAsync(SqlConnection connection, SqlTransaction transaction);

Task<InstallationNameAndVersion> GetAsync(string name, SqlConnection connection, SqlTransaction transaction);
Task<InstallationNameAndVersion?> GetAsync(string name, SqlConnection connection, SqlTransaction transaction);

Task<IEnumerable<InstallationNameAndVersion>> GetAllAsync(SqlConnection connection, SqlTransaction transaction);

Expand Down
4 changes: 2 additions & 2 deletions src/Rinsen.DatabaseInstaller/InstallVersionScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal class InstallVersionScope : IAsyncDisposable
private readonly SqlTransaction _transaction;
private readonly IVersionStorage _versionStorage;
private bool _failed = true;
private Exception _e;
private Exception? _e;

public InstallVersionScope(IVersionStorage versionStorage, DatabaseVersion databaseVersion, SqlConnection connection, SqlTransaction transaction)
{
Expand Down Expand Up @@ -42,7 +42,7 @@ public async ValueTask DisposeAsync()
}
}

private async Task<InstallationNameAndVersion> GetCurrentInstalledVersionAndValidatePostInstallationState()
private async Task<InstallationNameAndVersion?> GetCurrentInstalledVersionAndValidatePostInstallationState()
{
// Get installation row from database
var installedVersion = await _versionStorage.GetAsync(_databaseVersion.InstallationName, _connection, _transaction);
Expand Down
4 changes: 2 additions & 2 deletions src/Rinsen.DatabaseInstaller/InstallationNameAndVersion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ public class InstallationNameAndVersion
{
public int Id { get; set; }

public string InstallationName { get; set; }
public string InstallationName { get; set; } = string.Empty;

public int PreviousVersion { get; set; }

public int StartedInstallingVersion { get; set; }

public int InstalledVersion { get; set; }
}
}
}
8 changes: 1 addition & 7 deletions src/Rinsen.DatabaseInstaller/InstallationProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -186,13 +186,7 @@ private static ServiceProvider BootstrapApplication()
throw new InvalidOperationException("Schema is required in configuration");
}

serviceCollection.AddSingleton(new InstallerOptions
{
ConnectionStringName = connectionStringName,
ConnectionString = connectionString,
DatabaseName = databaseName,
Schema = schema
});
serviceCollection.AddSingleton(new InstallerOptions(databaseName, schema, connectionString, connectionStringName));

if (_databaseSetupType != null)
{
Expand Down
16 changes: 12 additions & 4 deletions src/Rinsen.DatabaseInstaller/InstallerOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,29 @@ public class InstallerOptions
/// <summary>
/// The database name to create or update.
/// </summary>
public string DatabaseName { get; set; }
public string DatabaseName { get; }

/// <summary>
/// The database schema name to create or update.
/// </summary>
public string Schema { get; set; }
public string Schema { get; }

/// <summary>
/// The connection string to the database server.
/// </summary>
public string ConnectionString { get; set; }
public string ConnectionString { get; }

/// <summary>
/// The connection string name in configuration to use for the database server.
/// </summary>
public string ConnectionStringName { get; internal set; }
public string ConnectionStringName { get; }

public InstallerOptions(string databaseName, string schema, string connectionString, string connectionStringName)
{
DatabaseName = databaseName;
Schema = schema;
ConnectionString = connectionString;
ConnectionStringName = connectionStringName;
}
}
}
Loading