Skip to content
This repository was archived by the owner on Aug 7, 2020. It is now read-only.

Commit c51ada8

Browse files
authored
Create the database on CI/CD (#17)
* Initial appveyor config * Move inline powershell and SQL to script files * Create the database * Bumped build image to VS 2015 for SQL 2016 Support * Refactored DB initialization code to Model assembly, and wrote a powershell script to create the migrations * Added SQL Backup Artifact * removed encryption from Log4Net DDL * Added lots of Env variables * quiet nuget output * Added a SQL script artifact to check for schema in the database in the absence of fully automated tests
1 parent cc914d0 commit c51ada8

25 files changed

+558
-219
lines changed

AlwaysEncryptedSample.Models/AlwaysEncryptedSample.Models.csproj

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,19 +101,42 @@
101101
</ItemGroup>
102102
<ItemGroup>
103103
<Compile Include="AccountViewModels.cs" />
104+
<Compile Include="ApplicationDbContext.cs" />
105+
<Compile Include="AuthDbContext.cs" />
104106
<Compile Include="ColumnInfo.cs" />
105107
<Compile Include="CreditCard.cs" />
106108
<Compile Include="CreditCardNetwork.cs" />
107109
<Compile Include="CreditCardNetworks.cs" />
110+
<Compile Include="DbInit.cs" />
108111
<Compile Include="IdentityModels.cs" />
109112
<Compile Include="ManageViewModels.cs" />
110113
<Compile Include="Properties\AssemblyInfo.cs" />
114+
<Compile Include="Properties\Resources.Designer.cs">
115+
<AutoGen>True</AutoGen>
116+
<DesignTime>True</DesignTime>
117+
<DependentUpon>Resources.resx</DependentUpon>
118+
</Compile>
119+
<Compile Include="Properties\Settings.Designer.cs">
120+
<AutoGen>True</AutoGen>
121+
<DesignTimeSharedInput>True</DesignTimeSharedInput>
122+
<DependentUpon>Settings.settings</DependentUpon>
123+
</Compile>
111124
</ItemGroup>
112125
<ItemGroup>
113126
<None Include="app.config" />
114127
<None Include="packages.config">
115128
<SubType>Designer</SubType>
116129
</None>
130+
<None Include="Properties\Settings.settings">
131+
<Generator>SettingsSingleFileGenerator</Generator>
132+
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
133+
</None>
134+
</ItemGroup>
135+
<ItemGroup>
136+
<EmbeddedResource Include="Properties\Resources.resx">
137+
<Generator>ResXFileCodeGenerator</Generator>
138+
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
139+
</EmbeddedResource>
117140
</ItemGroup>
118141
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
119142
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

AlwaysEncryptedSample/Services/ApplicationDbContext.cs renamed to AlwaysEncryptedSample.Models/ApplicationDbContext.cs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1-
using AlwaysEncryptedSample.Models;
2-
using AlwaysEncryptedSample.Properties;
1+
using System.Data.Entity;
2+
using AlwaysEncryptedSample.Models.Properties;
33

4-
namespace AlwaysEncryptedSample.Services
4+
namespace AlwaysEncryptedSample.Models
55
{
6-
using System.Data.Entity;
7-
86
public class ApplicationDbContext : DbContext
97
{
108
/// <remarks>Use DefaultConnection so we use one connection string for this and the ASP.NET authnetication.</remarks>
11-
public ApplicationDbContext()
12-
: base("name=DefaultConnection")
9+
public ApplicationDbContext(string nameOrConnectionString)
10+
: base(nameOrConnectionString)
1311
{
1412
}
1513

14+
public static ApplicationDbContext Create(string nameOrConnectionString = "DefaultConnection")
15+
{
16+
return new ApplicationDbContext(nameOrConnectionString);
17+
}
18+
1619
/// <remarks>We override this to set the schema.</remarks>
1720
protected override void OnModelCreating(DbModelBuilder modelBuilder)
1821
{

AlwaysEncryptedSample/Services/AuthDbContext.cs renamed to AlwaysEncryptedSample.Models/AuthDbContext.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
using System.Data.Entity;
2-
using AlwaysEncryptedSample.Models;
3-
using AlwaysEncryptedSample.Properties;
2+
using AlwaysEncryptedSample.Models.Properties;
43
using Microsoft.AspNet.Identity.EntityFramework;
54

6-
namespace AlwaysEncryptedSample.Services
5+
namespace AlwaysEncryptedSample.Models
76
{
87
public class AuthDbContext : IdentityDbContext<ApplicationUser>
98
{
10-
public AuthDbContext()
11-
: base("DefaultConnection", throwIfV1Schema: true)
9+
public AuthDbContext(string nameOrConnectionString)
10+
: base(nameOrConnectionString, throwIfV1Schema: true)
1211
{
1312
}
1413

1514
public static AuthDbContext Create()
1615
{
17-
return new AuthDbContext();
16+
return Create("name=DefaultConnection");
17+
}
18+
19+
public static AuthDbContext Create(string nameOrConnectionString)
20+
{
21+
return new AuthDbContext(nameOrConnectionString);
1822
}
1923

2024
/// <remarks>We override this to set the schema.</remarks>
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Data;
4+
using System.Data.SqlClient;
5+
using System.Linq;
6+
using System.Reflection;
7+
using System.Resources;
8+
using System.Text;
9+
using System.Threading.Tasks;
10+
using log4net;
11+
using Microsoft.AspNet.Identity;
12+
using Microsoft.AspNet.Identity.EntityFramework;
13+
14+
namespace AlwaysEncryptedSample.Models
15+
{
16+
public static class DbInit
17+
{
18+
private static ILog log = LogManager.GetLogger(typeof(DbInit));
19+
20+
/// <summary>
21+
/// Create the Authentication <see cref="System.Data.Entity.DbContext"/> if it does not exist.
22+
/// </summary>
23+
/// <param name="log"></param>
24+
public static void CreateAuthContext(string nameOrConnectionString = "DefaultConnection")
25+
{
26+
using (var authDbCtx = AuthDbContext.Create(nameOrConnectionString))
27+
{
28+
authDbCtx.Database.Log = (dbLog => log.Debug(dbLog));
29+
log.Info("Initialization tests for Authorization Schema");
30+
if (!authDbCtx.Roles.Any())
31+
{
32+
log.Info("No roles found in database. Creating roles");
33+
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(authDbCtx));
34+
roleManager.Create(new IdentityRole("DBAs")); // Gives database internals access
35+
roleManager.Create(new IdentityRole("Credit Card Admins")); // Gives access to CC info
36+
}
37+
38+
if (!authDbCtx.Users.Any())
39+
{
40+
log.Info("No users found in database. Creating users");
41+
var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(authDbCtx));
42+
userManager.Create(new ApplicationUser
43+
{
44+
Id = "Administrator",
45+
Email = "[email protected]",
46+
UserName = "Administrator",
47+
EmailConfirmed = true,
48+
PasswordHash = userManager.PasswordHasher.HashPassword("Alm0nds!"),
49+
});
50+
51+
userManager.AddToRole("Administrator", "DBAs");
52+
53+
userManager.Create(new ApplicationUser
54+
{
55+
Email = "[email protected]",
56+
Id = "CCAdmin",
57+
UserName = "CCAdmin",
58+
EmailConfirmed = true,
59+
PasswordHash = userManager.PasswordHasher.HashPassword("Appl3s")
60+
});
61+
userManager.AddToRole("CCAdmin", "Credit Card Admins");
62+
}
63+
64+
authDbCtx.SaveChanges();
65+
}
66+
}
67+
68+
/// <summary>
69+
/// Create the Purchasing <see cref="System.Data.Entity.DbContext"/> if it does not exist.
70+
/// </summary>
71+
/// <param name="log"></param>
72+
public static void CreatePurchasingContext(string nameOrConnectionString = "DefaultConnection")
73+
{
74+
using (var context = ApplicationDbContext.Create(nameOrConnectionString))
75+
{
76+
// TODO: Perhaps rethink doing this.
77+
// TODO: Consider using DatabaseLogFormatter to better format the logging.
78+
context.Database.Log = (dbLog => log.Debug(dbLog));
79+
log.Info("Initialization tests for Application Schema");
80+
//TODO: Could probably be sped up, but its O(n^2) where n = 4
81+
foreach (var newCCN in CreditCardNetwork.GetNetworks())
82+
{
83+
if (!context.CreditCardNetworks.Any(ccn => ccn.Id == newCCN.Id))
84+
{
85+
context.CreditCardNetworks.Add(newCCN);
86+
}
87+
}
88+
context.SaveChanges();
89+
}
90+
}
91+
92+
/// <summary>
93+
/// Creates a database if it doesn't exist.
94+
/// </summary>
95+
/// <param name="builder">Connection string builder to the database we want to create.</param>
96+
/// <returns>
97+
/// <c>true</c> if the database existed or we were able to create it, <c>false</c> if we could not try
98+
/// </returns>
99+
/// <remarks>
100+
///
101+
/// </remarks>
102+
public static bool CreateDatabase(SqlConnectionStringBuilder builder)
103+
{
104+
var dbName = builder.InitialCatalog;
105+
106+
// Can't connect to a db that doesn't already exist.
107+
builder.InitialCatalog = "tempdb";
108+
using (var cn = new SqlConnection(builder.ConnectionString))
109+
using (var cmd = cn.CreateCommand())
110+
{
111+
cmd.CommandText =
112+
"IF DB_ID(@dbName) IS NULL " +
113+
"EXEC ('CREATE DATABASE ' + @dbName + '; " +
114+
"ALTER DATABASE ' + @dbName + ' SET RECOVERY SIMPLE;')";
115+
cmd.Parameters.AddWithValue("@dbName", dbName);
116+
try
117+
{
118+
cn.Open();
119+
cmd.ExecuteNonQuery();
120+
}
121+
catch (SqlException)
122+
{
123+
//TODO: Send this exception somewhere.
124+
return false;
125+
}
126+
}
127+
return true;
128+
}
129+
130+
public static void InitLog4NetDb(IDbConnection cn)
131+
{
132+
//TODO: Make the database if it doesn't exist.
133+
var rm = new ResourceManager
134+
("AlwaysEncryptedSample.Models.Properties.Resources", Assembly.GetExecutingAssembly());
135+
var sql = rm.GetString("Log4NetDDL");
136+
using (var cmd = cn.CreateCommand())
137+
{
138+
/*
139+
* EF Code first does a good job of autocreating the database if it doesn't
140+
* exist. However, since we want log4net to log EF activity to the database
141+
* we have a chicken or egg problem.
142+
*/
143+
144+
cn.Open();
145+
/*
146+
* ADO.NET doesn't parse batches (read support the GO statement).
147+
* Because CREATE SCHEMA is extra picky we need this.
148+
*/
149+
cmd.CommandText = "SELECT COUNT(*) FROM sys.schemas WHERE name = 'Logging';";
150+
if ((int)cmd.ExecuteScalar() == 0)
151+
{
152+
cmd.CommandText = "CREATE SCHEMA [Logging]";
153+
cmd.ExecuteNonQuery();
154+
}
155+
cmd.CommandText = sql;
156+
cmd.ExecuteNonQuery();
157+
cn.Close();
158+
}
159+
}
160+
}
161+
}

AlwaysEncryptedSample.Models/Properties/Resources.Designer.cs

Lines changed: 86 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)