Browse Source

Created MySQL migration task to map old data to the new schema.

tags/2.0.3
Teknikode 4 years ago
parent
commit
e6097e2c07

+ 5
- 0
Teknik/Configuration/Config.cs View File

@@ -30,6 +30,7 @@ namespace Teknik.Configuration
private BlogConfig _BlogConfig;
private ApiConfig _ApiConfig;
private PodcastConfig _PodcastConfig;
private DatabaseConfig _DatabaseConfig;

public bool DevEnvironment { get { return _DevEnvironment; } set { _DevEnvironment = value; } }

@@ -70,6 +71,9 @@ namespace Teknik.Configuration
// Podcast Configuration
public PodcastConfig PodcastConfig { get { return _PodcastConfig; } set { _PodcastConfig = value; } }

// Database Configuration
public DatabaseConfig DatabaseConfig { get { return _DatabaseConfig; } set { _DatabaseConfig = value; } }

public Config()
{
_ConfigRWLock = new ReaderWriterLockSlim();
@@ -100,6 +104,7 @@ namespace Teknik.Configuration
PasteConfig = new PasteConfig();
ApiConfig = new ApiConfig();
PodcastConfig = new PodcastConfig();
DatabaseConfig = new DatabaseConfig();
}

public static Config Deserialize(string text)

+ 27
- 0
Teknik/Configuration/DatabaseConfig.cs View File

@@ -0,0 +1,27 @@
namespace Teknik.Configuration
{
public class DatabaseConfig
{
public bool Migrate { get; set; }
public string Server { get; set; }
public int Port { get; set; }
public string Database { get; set; }
public string Username { get; set; }
public string Password { get; set; }

public DatabaseConfig()
{
SetDefaults();
}

public void SetDefaults()
{
Migrate = false;
Server = "localhost";
Port = 3306;
Database = string.Empty;
Username = string.Empty;
Password = string.Empty;
}
}
}

+ 1
- 1
Teknik/Helpers/Constants.cs View File

@@ -9,7 +9,7 @@ namespace Teknik.Helpers
public static class Constants
{
// Blog Constants
public static int SERVERBLOGID = 1;
public static int SERVERBLOGID = 0;

// Paste Constants
public static Dictionary<string, string> HIGHLIGHTFORMATS = new Dictionary<string, string>()

+ 174
- 0
Teknik/Helpers/MysqlDatabase.cs View File

@@ -0,0 +1,174 @@
using System;
using System.Collections.Generic;
using System.Threading;
using Teknik.Configuration;
using MySql.Data.MySqlClient;

namespace Teknik.Helpers
{
public class MysqlDatabase
{
public event EventHandler<string> MysqlErrorEvent;

private bool Connected { get; set; }
private MySqlConnection Connection { get; set; }
private ReaderWriterLockSlim DatabaseLock { get; set; }

public MysqlDatabase(DatabaseConfig config)
{
Connected = false;
Connection = null;
DatabaseLock = new ReaderWriterLockSlim();
Connect(config);
}

public List<Dictionary<string, object>> Query(string query, params object[] args)
{
List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>();
if (Connected)
{
DatabaseLock.EnterWriteLock();
MySqlCommand cmd = PrepareQuery(query, args);
try
{
MySqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
Dictionary<string, object> row = new Dictionary<string, object>();
for (int i = 0; i < reader.FieldCount; i++)
{
row.Add(reader.GetName(i), reader.GetValue(i));
}
rows.Add(row);
}
reader.Close();
}
catch (MySqlException exception)
{
if (MysqlErrorEvent != null)
{
MysqlErrorEvent(this, exception.Message);
}
}
catch (Exception exception)
{
if (MysqlErrorEvent != null)
{
MysqlErrorEvent(this, exception.Message);
}
}
DatabaseLock.ExitWriteLock();
}
return rows;
}

public object ScalarQuery(string query, params object[] args)
{
if (Connected)
{
DatabaseLock.EnterWriteLock();
MySqlCommand cmd = PrepareQuery(query, args);
object result = null;
try
{

result = cmd.ExecuteScalar();
}
catch (MySqlException exception)
{
if (MysqlErrorEvent != null)
{
MysqlErrorEvent(this, exception.Message);
}
}
catch (Exception exception)
{
if (MysqlErrorEvent != null)
{
MysqlErrorEvent(this, exception.Message);
}
}
DatabaseLock.ExitWriteLock();
return result;
}
return null;
}

public void Execute(string query, params object[] args)
{
if (Connected)
{
DatabaseLock.EnterWriteLock();
MySqlCommand cmd = PrepareQuery(query, args);
try
{
int result = cmd.ExecuteNonQuery();
}
catch (MySqlException exception)
{
if (MysqlErrorEvent != null)
{
MysqlErrorEvent(this, exception.Message);
}
}
catch (Exception exception)
{
if (MysqlErrorEvent != null)
{
MysqlErrorEvent(this, exception.Message);
}
}
DatabaseLock.ExitWriteLock();
}
}

private void Connect(DatabaseConfig config)
{
if (Connection == null)
{
if (config.Server != string.Empty && config.Database != string.Empty && config.Username != string.Empty && config.Password != string.Empty)
{
string strCon = string.Format("Server={0}; database={1}; user={2}; password={3}; port={4}; charset=utf8", config.Server, config.Database, config.Username, config.Password, config.Port);
Connection = new MySqlConnection(strCon);
try
{
Connection.Open();
Connected = true;
}
catch (MySqlException ex)
{
Connected = false;
}
}
}
}

private void Disconnect()
{
if (Connection != null && Connected)
{
Connected = false;
Connection.Close();
}
}

private MySqlCommand PrepareQuery(string query, object[] args)
{
if (Connected)
{
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = Connection;
for (int i = 0; i < args.Length; i++)
{
string param = "{" + i + "}";
string paramName = "@DBVar_" + i;
query = query.Replace(param, paramName);
cmd.Parameters.AddWithValue(paramName, args[i]);
}
cmd.CommandText = query;
return cmd;
}
return null;
}
}
}

+ 186
- 14
Teknik/Migrations/Configuration.cs View File

@@ -1,11 +1,15 @@
namespace Teknik.Migrations
{
using Areas.Paste;
using Helpers;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
using Teknik.Configuration;

internal sealed class Configuration : DbMigrationsConfiguration<Teknik.Models.TeknikEntities>
internal sealed class Configuration : DbMigrationsConfiguration<Models.TeknikEntities>
{
public Configuration()
{
@@ -13,20 +17,188 @@ namespace Teknik.Migrations
AutomaticMigrationDataLossAllowed = true;
}

protected override void Seed(Teknik.Models.TeknikEntities context)
protected override void Seed(Models.TeknikEntities context)
{
// This method will be called after migrating to the latest version.

// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
//
Config config = Config.Load();
// Pre-populate with the default stuff

// Create server blog
Areas.Blog.Models.Blog serverBlog = new Areas.Blog.Models.Blog();
context.Blogs.Add(serverBlog);
context.SaveChanges();

// Create roles and groups
Areas.Profile.Models.Role adminRole = new Areas.Profile.Models.Role();
adminRole.Name = "Admin";
adminRole.Description = "Allows complete access to user specific actions";
context.Roles.Add(adminRole);

Areas.Profile.Models.Role podcastRole = new Areas.Profile.Models.Role();
podcastRole.Name = "Podcast";
podcastRole.Description = "Allows create/edit/delete access to podcasts";
context.Roles.Add(podcastRole);

Areas.Profile.Models.Group adminGroup = new Areas.Profile.Models.Group();
adminGroup.Name = "Administrators";
adminGroup.Description = "System Administrators with full access";
adminGroup.Roles.Add(adminRole);
adminGroup.Roles.Add(podcastRole);

Areas.Profile.Models.Group podcastGroup = new Areas.Profile.Models.Group();
podcastGroup.Name = "Podcast";
podcastGroup.Description = "Podcast team members";
podcastGroup.Roles.Add(podcastRole);

context.SaveChanges();


if (config.DatabaseConfig.Migrate && !config.DevEnvironment)
{
// Convert legacy MySQL DB to new MS SQL DB
MysqlDatabase db = new MysqlDatabase(config.DatabaseConfig);

// Transfer transactions
var transRet = db.Query("SELECT * FROM transactions");
foreach (var tran in transRet)
{
switch (tran["trans_type"].ToString())
{
case "One-Time":
Areas.Transparency.Models.OneTime tr = new Areas.Transparency.Models.OneTime();
tr.DateSent = DateTime.Parse(tran["date_posted"].ToString());
tr.Amount = Int32.Parse(tran["amount"].ToString());
tr.Currency = tran["currency"].ToString();
tr.Recipient = tran["recipient"].ToString();
tr.Reason = tran["reason"].ToString();
context.Transactions.Add(tr);
break;
case "Bill":
Areas.Transparency.Models.Bill bill = new Areas.Transparency.Models.Bill();
bill.DateSent = DateTime.Parse(tran["date_posted"].ToString());
bill.Amount = Int32.Parse(tran["amount"].ToString());
bill.Currency = tran["currency"].ToString();
bill.Recipient = tran["recipient"].ToString();
bill.Reason = tran["reason"].ToString();
context.Transactions.Add(bill);
break;
case "Donation":
Areas.Transparency.Models.Donation don = new Areas.Transparency.Models.Donation();
don.DateSent = DateTime.Parse(tran["date_posted"].ToString());
don.Amount = Int32.Parse(tran["amount"].ToString());
don.Currency = tran["currency"].ToString();
don.Sender = tran["sender"].ToString();
don.Reason = tran["reason"].ToString();
context.Transactions.Add(don);
break;
}
}
context.SaveChanges();

// Transfer Users and Blogs/Posts
Dictionary<int, int> userMapping = new Dictionary<int, int>();
Dictionary<int, int> postMapping = new Dictionary<int, int>();
var userRet = db.Query("SELECT * FROM users");
foreach (var user in userRet)
{
// Create User
Areas.Profile.Models.User newUser = new Areas.Profile.Models.User();
newUser.UserSettings = new Areas.Profile.Models.UserSettings();
newUser.BlogSettings = new Areas.Profile.Models.BlogSettings();
newUser.UploadSettings = new Areas.Profile.Models.UploadSettings();
newUser.TransferAccount = true;
newUser.Username = user["username"].ToString();
newUser.HashedPassword = user["password"].ToString();
newUser.JoinDate = DateTime.Parse(user["join_date"].ToString());
newUser.LastSeen = DateTime.Parse(user["last_seen"].ToString());
newUser.UserSettings.About = user["about"].ToString();
newUser.UserSettings.Website = user["website"].ToString();
newUser.UserSettings.Quote = user["quote"].ToString();
newUser.BlogSettings.Title = user["blog_title"].ToString();
newUser.BlogSettings.Description = user["blog_desc"].ToString();
context.Users.Add(newUser);
context.SaveChanges();
int userId = newUser.UserId;

userMapping.Add(Int32.Parse(user["id"].ToString()), userId);

// Create Blog for user
Areas.Blog.Models.Blog newBlog = new Areas.Blog.Models.Blog();
newBlog.UserId = userId;
context.SaveChanges();
int blogId = newBlog.BlogId;

// Transfer Blog Posts
var postRet = db.Query("SELECT * FROM blog WHERE author_id={0}", new object[] { userId });
if (postRet != null)
{
foreach (var post in postRet)
{
// Create new Blog Post
Areas.Blog.Models.BlogPost newPost = new Areas.Blog.Models.BlogPost();
if (post["user_id"].ToString() == "0")
{
newPost.BlogId = 0;
newPost.System = true;
}
else
{
newPost.BlogId = blogId;
}
newPost.DatePosted = DateTime.Parse(post["date_posted"].ToString());
newPost.DatePublished = DateTime.Parse(post["date_published"].ToString());
newPost.DateEdited = DateTime.Parse(post["date_published"].ToString());
newPost.Published = (post["published"].ToString() == "1");
newPost.Title = post["title"].ToString();
newPost.Article = post["post"].ToString();
context.BlogPosts.Add(newPost);
context.SaveChanges();
postMapping.Add(Int32.Parse(post["id"].ToString()), newPost.BlogPostId);
}
}
}

// Transfer Blog Comments
var commentRet = db.Query("SELECT * FROM comments WHERE service = 'blog'");
foreach (var comment in commentRet)
{
int postId = Int32.Parse(comment["reply_id"].ToString());
int userId = Int32.Parse(comment["user_id"].ToString());
if (postMapping.ContainsKey(postId) && userMapping.ContainsKey(userId))
{
Areas.Blog.Models.BlogPostComment newComment = new Areas.Blog.Models.BlogPostComment();
newComment.BlogPostId = postMapping[postId];
newComment.UserId = userMapping[userId];
newComment.Article = comment["post"].ToString();
newComment.DatePosted = DateTime.Parse(comment["date_posted"].ToString());
newComment.DateEdited = DateTime.Parse(comment["date_posted"].ToString());
context.BlogComments.Add(newComment);
context.SaveChanges();
}
}

// Transfer Pastes
var pasteRet = db.Query("SELECT * FROM paste");
foreach (var paste in pasteRet)
{
// If it's a password protected paste, we just skip it
if (paste["password"] == null)
{
string content = paste["code"].ToString();
string title = paste["title"].ToString();
DateTime posted = DateTime.Parse(paste["posted"].ToString());
int userId = Int32.Parse(paste["user_id"].ToString());
Areas.Paste.Models.Paste newPaste = PasteHelper.CreatePaste(content, title);
newPaste.DatePosted = posted;
newPaste.Url = paste["pid"].ToString();
if (userMapping.ContainsKey(userId) && userId != 0)
{
newPaste.UserId = userMapping[userId];
}
context.Pastes.Add(newPaste);
context.SaveChanges();
}
}
}
}
}
}

+ 6
- 0
Teknik/Teknik.csproj View File

@@ -72,6 +72,10 @@
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="MySql.Data, Version=6.9.8.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL">
<HintPath>..\packages\MySql.Data.6.9.8\lib\net45\MySql.Data.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
@@ -219,6 +223,7 @@
<Compile Include="Areas\Upload\ViewModels\DownloadViewModel.cs" />
<Compile Include="Areas\Upload\ViewModels\UploadViewModel.cs" />
<Compile Include="Configuration\ApiConfig.cs" />
<Compile Include="Configuration\DatabaseConfig.cs" />
<Compile Include="Configuration\EmailConfig.cs" />
<Compile Include="Configuration\GitConfig.cs" />
<Compile Include="Configuration\PodcastConfig.cs" />
@@ -241,6 +246,7 @@
<Compile Include="Areas\Profile\Models\PermissionType.cs" />
<Compile Include="Areas\Blog\Models\BlogPost.cs" />
<Compile Include="Areas\Profile\Models\Role.cs" />
<Compile Include="Helpers\MysqlDatabase.cs" />
<Compile Include="Helpers\MarkdownHelper.cs" />
<Compile Include="Helpers\RSSFeedResult.cs" />
<Compile Include="Helpers\UrlExtensions.cs" />

+ 6
- 1
Teknik/Web.config View File

@@ -85,4 +85,9 @@
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
</configuration>
<system.data>
<DbProviderFactories>
<remove invariant="MySql.Data.MySqlClient" />
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.9.8.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
</DbProviderFactories>
</system.data></configuration>

+ 1
- 0
Teknik/packages.config View File

@@ -22,6 +22,7 @@
<package id="Microsoft.jQuery.Unobtrusive.Validation" version="3.2.3" targetFramework="net46" userInstalled="true" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net46" userInstalled="true" />
<package id="Modernizr" version="2.8.3" targetFramework="net452" userInstalled="true" />
<package id="MySql.Data" version="6.9.8" targetFramework="net452" />
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net452" userInstalled="true" />
<package id="Respond" version="1.4.2" targetFramework="net452" userInstalled="true" />
<package id="routedebugger" version="2.1.4" targetFramework="net452" userInstalled="true" />

Loading…
Cancel
Save