Rework database loading/migration

This commit is contained in:
2023-04-06 17:20:38 -04:00
parent b5f570688d
commit 6514f23329
21 changed files with 561 additions and 569 deletions

View File

@@ -44,9 +44,11 @@ namespace FeedCenter
using (isolationHandle)
{
// Set the path
LegacyDatabase.DatabasePath = SystemConfiguration.DataDirectory;
LegacyDatabase.DatabaseFile = Path.Combine(SystemConfiguration.DataDirectory, Settings.Default.DatabaseFile_Legacy);
Database.DatabasePath = SystemConfiguration.DataDirectory;
Database.DatabaseFile = Path.Combine(SystemConfiguration.DataDirectory, Settings.Default.DatabaseFile);
Database.Load();
// Get the generic provider
var genericProvider = (GenericSettingsProvider) Settings.Default.Providers[nameof(GenericSettingsProvider)];

View File

@@ -1,218 +1,25 @@
using FeedCenter.Properties;
using Serilog;
using System;
using System.Collections.Generic;
using System.Data.SqlServerCe;
using System.IO;
using System.Linq;
using System.IO;
namespace FeedCenter.Data
{
public static class Database
{
#region Static database settings
public static string DatabaseFile { get; set; }
public static string DatabasePath { get; set; }
public static string DatabaseFile;
public static string DatabasePath;
public static FeedCenterEntities Entities { get; set; }
#endregion
public static bool Exists => File.Exists(DatabaseFile);
#region File version
public static FeedCenterEntities Entities;
private enum SqlServerCeFileVersion
{
Unknown,
Version20,
Version30,
Version35,
Version40,
}
public static bool Loaded { get; set; }
public static void Load()
{
if (Loaded) return;
Entities = new FeedCenterEntities();
}
private static SqlServerCeFileVersion GetFileVersion(string databasePath)
{
// Create a mapping of version numbers to the version enumeration
var versionMapping = new Dictionary<int, SqlServerCeFileVersion>
{
{ 0x73616261, SqlServerCeFileVersion.Version20 },
{ 0x002dd714, SqlServerCeFileVersion.Version30 },
{ 0x00357b9d, SqlServerCeFileVersion.Version35 },
{ 0x003d0900, SqlServerCeFileVersion.Version40 }
};
int signature;
try
{
// Open the database file
using var stream = new FileStream(databasePath, FileMode.Open, FileAccess.Read);
// Read the file using the binary reader
var reader = new BinaryReader(stream);
// Seek to the version signature
stream.Seek(16, SeekOrigin.Begin);
// Read the version signature
signature = reader.ReadInt32();
}
catch (Exception exception)
{
Log.Logger.Error(exception, "Exception");
throw;
}
// If we know about the version number then return the right enumeration - otherwise unknown
return versionMapping.ContainsKey(signature) ? versionMapping[signature] : SqlServerCeFileVersion.Unknown;
}
#endregion
public static bool DatabaseExists => File.Exists(DatabaseFile);
public static void CreateDatabase()
{
Log.Logger.Information("Creating database engine");
// Create the database engine
using var engine = new SqlCeEngine($"Data Source={DatabaseFile}");
Log.Logger.Information("Creating database");
// Create the database itself
engine.CreateDatabase();
Log.Logger.Information("Running database script");
// Run the creation script
ExecuteScript(Resources.CreateDatabase);
}
private static int GetVersion(SqlCeConnection connection)
{
string versionString;
try
{
// Check the database version table
using var command = new SqlCeCommand("SELECT Value FROM DatabaseVersion", connection);
versionString = command.ExecuteScalar().ToString();
}
catch (SqlCeException)
{
// Check the setting table for the version
using var command = new SqlCeCommand("SELECT Value FROM Setting WHERE Name = 'DatabaseVersion'", connection);
versionString = command.ExecuteScalar().ToString();
}
if (string.IsNullOrEmpty(versionString))
versionString = "0";
Log.Logger.Information("Database version: {0}", versionString);
return int.Parse(versionString);
}
public static void UpdateDatabase()
{
Log.Logger.Information("Getting database file version");
// Get the database file version
var fileVersion = GetFileVersion(DatabaseFile);
Log.Logger.Information("Database file version: {0}", fileVersion);
// See if we need to upgrade the database file version
if (fileVersion != SqlServerCeFileVersion.Version40)
{
Log.Logger.Information("Creating database engine");
// Create the database engine
using var engine = new SqlCeEngine($"Data Source={DatabaseFile}");
Log.Logger.Information("Upgrading database");
// Upgrade the database (if needed)
engine.Upgrade();
}
Log.Logger.Information("Getting database version");
// Create a database connection
using var connection = new SqlCeConnection($"Data Source={DatabaseFile}");
// Open the connection
connection.Open();
// Get the database version
var databaseVersion = GetVersion(connection);
// Create a dictionary of database upgrade scripts and their version numbers
var scriptList = new Dictionary<int, string>();
// Loop over the properties of the resource object looking for update scripts
foreach (var property in typeof(Resources).GetProperties().Where(property => property.Name.StartsWith("DatabaseUpdate")))
{
// Get the name of the property
var propertyName = property.Name;
// Extract the version from the name
var version = int.Parse(propertyName.Substring(propertyName.IndexOf("_", StringComparison.Ordinal) + 1));
// Add to the script list
scriptList[version] = propertyName;
}
// Loop over the scripts ordered by version
foreach (var pair in scriptList.OrderBy(pair => pair.Key))
{
// If the database version is less than or equal to the script version the script needs to run
if (databaseVersion <= pair.Key)
{
// Get the script text
var scriptText = Resources.ResourceManager.GetString(pair.Value);
// Run the script
ExecuteScript(scriptText);
}
}
}
public static void MaintainDatabase()
{
Log.Logger.Information("Creating database engine");
// Create the database engine
using var engine = new SqlCeEngine($"Data Source={DatabaseFile}");
Log.Logger.Information("Shrinking database");
// Compact the database
engine.Shrink();
}
private static void ExecuteScript(string scriptText)
{
// Create a database connection
using var connection = new SqlCeConnection($"Data Source={DatabaseFile}");
// Open the connection
connection.Open();
// Setup the delimiters
var delimiters = new[] { "\r\nGO\r\n" };
// Split the script at the delimiters
var statements = scriptText.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);
// Loop over each statement in the script
foreach (var statement in statements)
{
// Execute the statement
using var command = new SqlCeCommand(statement, connection);
command.ExecuteNonQuery();
}
Loaded = true;
}
}
}

View File

@@ -1,13 +0,0 @@
using System.Data.SqlTypes;
namespace FeedCenter.Data
{
public static class Extensions
{
#region SqlDateTime
public static SqlDateTime SqlDateTimeZero = new SqlDateTime(0, 0);
#endregion
}
}

View File

@@ -0,0 +1,234 @@
using Dapper;
using FeedCenter.Options;
using FeedCenter.Properties;
using Realms;
using Serilog;
using System;
using System.Collections.Generic;
using System.Data.SqlServerCe;
using System.IO;
using System.Linq;
namespace FeedCenter.Data;
public static class LegacyDatabase
{
public static string DatabaseFile { get; set; }
public static string DatabasePath { get; set; }
private enum SqlServerCeFileVersion
{
Unknown,
Version20,
Version30,
Version35,
Version40,
}
private static SqlServerCeFileVersion GetFileVersion(string databasePath)
{
// Create a mapping of version numbers to the version enumeration
var versionMapping = new Dictionary<int, SqlServerCeFileVersion>
{
{ 0x73616261, SqlServerCeFileVersion.Version20 },
{ 0x002dd714, SqlServerCeFileVersion.Version30 },
{ 0x00357b9d, SqlServerCeFileVersion.Version35 },
{ 0x003d0900, SqlServerCeFileVersion.Version40 }
};
int signature;
try
{
// Open the database file
using var stream = new FileStream(databasePath, FileMode.Open, FileAccess.Read);
// Read the file using the binary reader
var reader = new BinaryReader(stream);
// Seek to the version signature
stream.Seek(16, SeekOrigin.Begin);
// Read the version signature
signature = reader.ReadInt32();
}
catch (Exception exception)
{
Log.Logger.Error(exception, "Exception");
throw;
}
// If we know about the version number then return the right enumeration - otherwise unknown
return versionMapping.TryGetValue(signature, out var value) ? value : SqlServerCeFileVersion.Unknown;
}
public static bool Exists => File.Exists(DatabaseFile);
private static int GetVersion(SqlCeConnection connection)
{
string versionString;
try
{
// Check the database version table
using var command = new SqlCeCommand("SELECT Value FROM DatabaseVersion", connection);
versionString = command.ExecuteScalar().ToString();
}
catch (SqlCeException)
{
// Check the setting table for the version
using var command = new SqlCeCommand("SELECT Value FROM Setting WHERE Name = 'DatabaseVersion'", connection);
versionString = command.ExecuteScalar().ToString();
}
if (string.IsNullOrEmpty(versionString))
versionString = "0";
Log.Logger.Information("Database version: {0}", versionString);
return int.Parse(versionString);
}
public static void UpdateDatabase()
{
Log.Logger.Information("Getting database file version");
// Get the database file version
var fileVersion = GetFileVersion(DatabaseFile);
Log.Logger.Information("Database file version: {0}", fileVersion);
// See if we need to upgrade the database file version
if (fileVersion != SqlServerCeFileVersion.Version40)
{
Log.Logger.Information("Creating database engine");
// Create the database engine
using var engine = new SqlCeEngine($"Data Source={DatabaseFile}");
Log.Logger.Information("Upgrading database");
// Upgrade the database (if needed)
engine.Upgrade();
}
Log.Logger.Information("Getting database version");
// Create a database connection
using var connection = new SqlCeConnection($"Data Source={DatabaseFile}");
// Open the connection
connection.Open();
// Get the database version
var databaseVersion = GetVersion(connection);
// Create a dictionary of database upgrade scripts and their version numbers
var scriptList = new Dictionary<int, string>();
// Loop over the properties of the resource object looking for update scripts
foreach (var property in typeof(Resources).GetProperties().Where(property => property.Name.StartsWith("DatabaseUpdate")))
{
// Get the name of the property
var propertyName = property.Name;
// Extract the version from the name
var version = int.Parse(propertyName[(propertyName.IndexOf("_", StringComparison.Ordinal) + 1)..]);
// Add to the script list
scriptList[version] = propertyName;
}
// Loop over the scripts ordered by version
foreach (var pair in scriptList.OrderBy(pair => pair.Key))
{
// If the database version is beyond this script then we can skip it
if (databaseVersion > pair.Key) continue;
// Get the script text
var scriptText = Resources.ResourceManager.GetString(pair.Value);
// Run the script
ExecuteScript(scriptText);
}
}
public static void MaintainDatabase()
{
Log.Logger.Information("Creating database engine");
// Create the database engine
using var engine = new SqlCeEngine($"Data Source={DatabaseFile}");
Log.Logger.Information("Shrinking database");
// Compact the database
engine.Shrink();
}
public static void MigrateDatabase()
{
var realmConfiguration = new RealmConfiguration($"{Database.DatabaseFile}");
var realm = Realm.GetInstance(realmConfiguration);
if (!File.Exists(DatabaseFile))
return;
using var connection = new SqlCeConnection($"Data Source={DatabaseFile}");
connection.Open();
var settings = connection.Query<Setting>("SELECT * FROM Setting").OrderBy(s => s.Version).ToList();
var categories = connection.Query<Category>("SELECT * FROM Category").ToList();
var feeds = connection.Query<Feed>("SELECT * FROM Feed").ToList();
var feedItems = connection.Query<FeedItem>("SELECT * FROM FeedItem").ToList();
realm.Write(() =>
{
foreach (var category in categories)
{
category.Feeds = feeds.Where(f => f.CategoryId == category.Id).ToList();
}
foreach (var feed in feeds)
{
feed.Category = categories.FirstOrDefault(c => c.Id == feed.CategoryId);
}
foreach (var feedItem in feedItems)
{
var feed = feeds.First(f => f.Id == feedItem.FeedId);
feed.Items.Add(feedItem);
}
realm.Add(feeds);
realm.Add(categories);
realm.Add(settings, true);
});
connection.Close();
File.Move(DatabaseFile, DatabaseFile + "_bak");
}
private static void ExecuteScript(string scriptText)
{
// Create a database connection
using var connection = new SqlCeConnection($"Data Source={DatabaseFile}");
// Open the connection
connection.Open();
// Setup the delimiters
var delimiters = new[] { "\r\nGO\r\n" };
// Split the script at the delimiters
var statements = scriptText.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);
// Loop over each statement in the script
foreach (var statement in statements)
{
// Execute the statement
using var command = new SqlCeCommand(statement, connection);
command.ExecuteNonQuery();
}
}
}

View File

@@ -1,10 +1,7 @@
using Dapper;
using FeedCenter.Data;
using FeedCenter.Data;
using FeedCenter.Options;
using Realms;
using System;
using System.Data.SqlServerCe;
using System.IO;
using System.Linq;
namespace FeedCenter
@@ -19,60 +16,10 @@ namespace FeedCenter
public FeedCenterEntities()
{
Load();
}
public void Refresh()
{
Realm.Refresh();
}
public void Load()
{
var realmConfiguration = new RealmConfiguration($"{Database.DatabasePath}/FeedCenter.realm");
var realmConfiguration = new RealmConfiguration($"{Database.DatabaseFile}");
Realm = Realm.GetInstance(realmConfiguration);
if (File.Exists(Database.DatabaseFile))
{
using var connection = new SqlCeConnection($"Data Source={Database.DatabaseFile}");
connection.Open();
var settings = connection.Query<Setting>("SELECT * FROM Setting").OrderBy(s => s.Version).ToList();
var categories = connection.Query<Category>("SELECT * FROM Category").ToList();
var feeds = connection.Query<Feed>("SELECT * FROM Feed").ToList();
var feedItems = connection.Query<FeedItem>("SELECT * FROM FeedItem").ToList();
Realm.Write(() =>
{
foreach (var category in categories)
{
category.Feeds = feeds.Where(f => f.CategoryId == category.Id).ToList();
}
foreach (var feed in feeds)
{
feed.Category = categories.FirstOrDefault(c => c.Id == feed.CategoryId);
}
foreach (var feedItem in feedItems)
{
var feed = feeds.First(f => f.Id == feedItem.FeedId);
feed.Items.Add(feedItem);
}
Realm.Add(feeds);
Realm.Add(categories);
Realm.Add(settings, true);
});
connection.Close();
File.Move(Database.DatabaseFile, Database.DatabaseFile + "_bak");
}
Settings = new RealmObservableCollection<Setting>(Realm);
Feeds = new RealmObservableCollection<Feed>(Realm);
Categories = new RealmObservableCollection<Category>(Realm);
@@ -83,6 +30,11 @@ namespace FeedCenter
}
}
public void Refresh()
{
Realm.Refresh();
}
public void SaveChanges(Action action)
{
Realm.Write(action);

View File

@@ -157,12 +157,23 @@
<Compile Remove="SqlSettingsProvider.cs" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Update="Properties\Settings.Designer.cs">
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Update="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>

View File

@@ -108,13 +108,14 @@ namespace FeedCenter
Close();
}
private async void HandleRefreshCurrentButtonClick(object sender, RoutedEventArgs e)
private void HandleRefreshCurrentButtonClick(object sender, RoutedEventArgs e)
{
IsEnabled = false;
Mouse.OverrideCursor = Cursors.Wait;
var feed = (Feed) FeedDataGrid.SelectedItem;
await feed.ReadAsync(_database, true);
_database.SaveChanges(() => feed.Read(_database, true));
var selectedIndex = FeedDataGrid.SelectedIndex;

View File

@@ -72,8 +72,8 @@ namespace FeedCenter
public string Link { get; set; }
public string Description { get; set; }
public DateTimeOffset LastChecked { get; set; }
public int CheckInterval { get; set; }
public bool Enabled { get; set; }
public int CheckInterval { get; set; } = 60;
public bool Enabled { get; set; } = true;
public bool Authenticate { get; set; }
public string Username { get; set; }
public string Password { get; set; }
@@ -164,7 +164,7 @@ namespace FeedCenter
}
// If the feed was successfully read and we have no last update timestamp - set the last update timestamp to now
if (result == FeedReadResult.Success && LastUpdated == Extensions.SqlDateTimeZero.Value)
if (result == FeedReadResult.Success && LastUpdated == default)
LastUpdated = DateTimeOffset.Now;
Log.Logger.Information("Done reading feed: {0}", result);
@@ -172,11 +172,6 @@ namespace FeedCenter
return result;
}
public async Task<FeedReadResult> ReadAsync(FeedCenterEntities database, bool forceRead = false)
{
return await Task.Run(() => Read(database, forceRead));
}
public Tuple<FeedType, string> DetectFeedType()
{
var retrieveResult = RetrieveFeed();

View File

@@ -23,7 +23,6 @@ namespace FeedCenter
var feed = Feed.Create(_database);
feed.Source = feedUrl;
feed.Category = _database.DefaultCategory;
feed.Enabled = true;
// Try to detect the feed type
var feedTypeResult = feed.DetectFeedType();

View File

@@ -15,8 +15,23 @@ namespace FeedCenter
private class FeedReadWorkerInput
{
public bool ForceRead;
public Feed Feed;
public bool ForceRead { get; }
public Guid? FeedId { get; }
public FeedReadWorkerInput()
{
}
public FeedReadWorkerInput(bool forceRead)
{
ForceRead = forceRead;
}
public FeedReadWorkerInput(bool forceRead, Guid? feedId)
{
ForceRead = forceRead;
FeedId = feedId;
}
}
private void SetProgressMode(bool value, int feedCount)
@@ -48,7 +63,7 @@ namespace FeedCenter
SetProgressMode(true, 1);
// Create the input class
var workerInput = new FeedReadWorkerInput { ForceRead = forceRead, Feed = _currentFeed };
var workerInput = new FeedReadWorkerInput(forceRead, _currentFeed.Id);
// Start the worker
_feedReadWorker.RunWorkerAsync(workerInput);
@@ -68,7 +83,7 @@ namespace FeedCenter
SetProgressMode(true, _database.Feeds.Count);
// Create the input class
var workerInput = new FeedReadWorkerInput { ForceRead = forceRead, Feed = null };
var workerInput = new FeedReadWorkerInput(forceRead);
// Start the worker
_feedReadWorker.RunWorkerAsync(workerInput);
@@ -126,7 +141,7 @@ namespace FeedCenter
var worker = (BackgroundWorker) sender;
// Get the input information
var workerInput = (FeedReadWorkerInput) e.Argument ?? new FeedReadWorkerInput { Feed = null, ForceRead = false };
var workerInput = (FeedReadWorkerInput) e.Argument ?? new FeedReadWorkerInput();
// Setup for progress
var currentProgress = 0;
@@ -135,8 +150,8 @@ namespace FeedCenter
var feedsToRead = new List<Feed>();
// If we have a single feed then add it to the list - otherwise add them all
if (workerInput.Feed != null)
feedsToRead.Add(database.Feeds.First(feed => feed.Id == workerInput.Feed.Id));
if (workerInput.FeedId != null)
feedsToRead.Add(database.Feeds.First(feed => feed.Id == workerInput.FeedId));
else
feedsToRead.AddRange(database.Feeds);

View File

@@ -29,7 +29,7 @@ namespace FeedCenter.Options
private void HandleOkayButtonClick(object sender, RoutedEventArgs e)
{
if (!this.Validate())
if (!this.IsValid())
return;
// Dialog is good

View File

@@ -54,8 +54,6 @@ namespace FeedCenter.Options
var expressions = this.GetBindingExpressions(new[] { UpdateSourceTrigger.Explicit });
this.UpdateAllSources(expressions);
this.Validate();
}
public override string CategoryName => Properties.Resources.optionCategoryGeneral;
@@ -106,22 +104,22 @@ namespace FeedCenter.Options
{
var userAgents = new List<UserAgentItem>
{
new UserAgentItem
new()
{
Caption = Properties.Resources.DefaultUserAgentCaption,
UserAgent = string.Empty
},
new UserAgentItem
new()
{
Caption = "Windows RSS Platform 2.0",
UserAgent = "Windows-RSS-Platform/2.0 (MSIE 9.0; Windows NT 6.1)"
},
new UserAgentItem
new()
{
Caption = "Feedly 1.0",
UserAgent = "Feedly/1.0"
},
new UserAgentItem
new()
{
Caption = "curl",
UserAgent = "curl/7.47.0"

View File

@@ -9,7 +9,7 @@ namespace FeedCenter.Options
{
#region Member variables
private readonly List<OptionsPanelBase> _optionPanels = new List<OptionsPanelBase>();
private readonly List<OptionsPanelBase> _optionPanels = new();
private readonly FeedCenterEntities _database = Database.Entities;

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is automatically generated by Visual Studio .Net. It is
used to store generic object data source configuration information.
Renaming the file extension or editing the content of this file may
cause the file to be unrecognizable by the program.
-->
<GenericObjectDataSource DisplayName="FeedCenterEntities" Identifier="FeedCenter.FeedCenterEntities" ProviderType="Microsoft.VisualStudio.DataDesign.DataSourceProviders.EntityDataModel.EdmDataSourceProvider" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource">
<TypeInfo>FeedCenter.FeedCenterEntities, Model.Designer.cs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo>
</GenericObjectDataSource>

View File

@@ -1298,11 +1298,11 @@ namespace FeedCenter.Properties {
}
/// <summary>
/// Looks up a localized string similar to Checking database existence....
/// Looks up a localized string similar to Checking for legacy database....
/// </summary>
public static string SplashCheckingForDatabase {
public static string SplashCheckingForLegacyDatabase {
get {
return ResourceManager.GetString("SplashCheckingForDatabase", resourceCulture);
return ResourceManager.GetString("SplashCheckingForLegacyDatabase", resourceCulture);
}
}
@@ -1343,11 +1343,29 @@ namespace FeedCenter.Properties {
}
/// <summary>
/// Looks up a localized string similar to Maintaining database....
/// Looks up a localized string similar to Loading database....
/// </summary>
public static string SplashMaintainingDatabase {
public static string SplashLoadingDatabase {
get {
return ResourceManager.GetString("SplashMaintainingDatabase", resourceCulture);
return ResourceManager.GetString("SplashLoadingDatabase", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Maintaining legacy database....
/// </summary>
public static string SplashMaintainingLegacyDatabase {
get {
return ResourceManager.GetString("SplashMaintainingLegacyDatabase", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Migrating legacy database....
/// </summary>
public static string SplashMigratingLegacyDatabase {
get {
return ResourceManager.GetString("SplashMigratingLegacyDatabase", resourceCulture);
}
}
@@ -1370,11 +1388,11 @@ namespace FeedCenter.Properties {
}
/// <summary>
/// Looks up a localized string similar to Updating database....
/// Looks up a localized string similar to Updating legacy database....
/// </summary>
public static string SplashUpdatingDatabase {
public static string SplashUpdatingLegacyDatabase {
get {
return ResourceManager.GetString("SplashUpdatingDatabase", resourceCulture);
return ResourceManager.GetString("SplashUpdatingLegacyDatabase", resourceCulture);
}
}

View File

@@ -154,8 +154,8 @@
<data name="SplashCheckingForUpdate" xml:space="preserve">
<value>Checking for update...</value>
</data>
<data name="SplashCheckingForDatabase" xml:space="preserve">
<value>Checking database existence...</value>
<data name="SplashCheckingForLegacyDatabase" xml:space="preserve">
<value>Checking for legacy database...</value>
</data>
<data name="SplashCreatingDatabase" xml:space="preserve">
<value>Creating database...</value>
@@ -169,11 +169,11 @@
<data name="SplashDownloadingUpdate" xml:space="preserve">
<value>Downloading update...</value>
</data>
<data name="SplashMaintainingDatabase" xml:space="preserve">
<value>Maintaining database...</value>
<data name="SplashMaintainingLegacyDatabase" xml:space="preserve">
<value>Maintaining legacy database...</value>
</data>
<data name="SplashUpdatingDatabase" xml:space="preserve">
<value>Updating database...</value>
<data name="SplashUpdatingLegacyDatabase" xml:space="preserve">
<value>Updating legacy database...</value>
</data>
<data name="SplashStarting" xml:space="preserve">
<value>Starting...</value>
@@ -529,4 +529,10 @@
<data name="defaultUserAgentLabel" xml:space="preserve">
<value>Default _user agent:</value>
</data>
<data name="SplashLoadingDatabase" xml:space="preserve">
<value>Loading database...</value>
</data>
<data name="SplashMigratingLegacyDatabase" xml:space="preserve">
<value>Migrating legacy database...</value>
</data>
</root>

View File

@@ -74,9 +74,9 @@ namespace FeedCenter.Properties {
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("FeedCenter.sdf")]
public string DatabaseFile {
public string DatabaseFile_Legacy {
get {
return ((string)(this["DatabaseFile"]));
return ((string)(this["DatabaseFile_Legacy"]));
}
}
@@ -293,5 +293,14 @@ namespace FeedCenter.Properties {
this["DefaultUserAgent"] = value;
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("FeedCenter.realm")]
public string DatabaseFile {
get {
return ((string)(this["DatabaseFile"]));
}
}
}
}

View File

@@ -2,19 +2,19 @@
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="FeedCenter.Properties" GeneratedClassName="Settings">
<Profiles />
<Settings>
<Setting Name="WindowLocked" Provider="Common.Settings.GenericSettingsProvider" Type="System.Boolean" Scope="User">
<Setting Name="WindowLocked" Provider="CKaczor.GenericSettingsProvider.GenericSettingsProvider" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="WindowSize" Provider="Common.Settings.GenericSettingsProvider" Type="System.Windows.Size" Scope="User">
<Setting Name="WindowSize" Provider="CKaczor.GenericSettingsProvider.GenericSettingsProvider" Type="System.Windows.Size" Scope="User">
<Value Profile="(Default)">0,0</Value>
</Setting>
<Setting Name="WindowLocation" Provider="Common.Settings.GenericSettingsProvider" Type="System.Windows.Point" Scope="User">
<Setting Name="WindowLocation" Provider="CKaczor.GenericSettingsProvider.GenericSettingsProvider" Type="System.Windows.Point" Scope="User">
<Value Profile="(Default)">0,0</Value>
</Setting>
<Setting Name="LogDatabase" Type="System.Boolean" Scope="Application">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="DatabaseFile" Type="System.String" Scope="Application">
<Setting Name="DatabaseFile_Legacy" Type="System.String" Scope="Application">
<Value Profile="(Default)">FeedCenter.sdf</Value>
</Setting>
<Setting Name="ProgressSleepInterval" Type="System.Int32" Scope="Application">
@@ -26,40 +26,40 @@
<Setting Name="BalloonTipTimeout" Type="System.Int32" Scope="Application">
<Value Profile="(Default)">5000</Value>
</Setting>
<Setting Name="FeedScrollInterval" Provider="Common.Settings.GenericSettingsProvider" Type="System.TimeSpan" Scope="User">
<Setting Name="FeedScrollInterval" Provider="CKaczor.GenericSettingsProvider.GenericSettingsProvider" Type="System.TimeSpan" Scope="User">
<Value Profile="(Default)">00:01:00</Value>
</Setting>
<Setting Name="FeedCheckInterval" Provider="Common.Settings.GenericSettingsProvider" Type="System.TimeSpan" Scope="User">
<Setting Name="FeedCheckInterval" Provider="CKaczor.GenericSettingsProvider.GenericSettingsProvider" Type="System.TimeSpan" Scope="User">
<Value Profile="(Default)">00:30:00</Value>
</Setting>
<Setting Name="DisplayEmptyFeeds" Provider="Common.Settings.GenericSettingsProvider" Type="System.Boolean" Scope="User">
<Setting Name="DisplayEmptyFeeds" Provider="CKaczor.GenericSettingsProvider.GenericSettingsProvider" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="LastVersionCheck" Provider="Common.Settings.GenericSettingsProvider" Type="System.DateTime" Scope="User">
<Setting Name="LastVersionCheck" Provider="CKaczor.GenericSettingsProvider.GenericSettingsProvider" Type="System.DateTime" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="VersionCheckInterval" Type="System.TimeSpan" Scope="Application">
<Value Profile="(Default)">01:00:00</Value>
</Setting>
<Setting Name="StartWithWindows" Provider="Common.Settings.GenericSettingsProvider" Type="System.Boolean" Scope="User">
<Setting Name="StartWithWindows" Provider="CKaczor.GenericSettingsProvider.GenericSettingsProvider" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="FirstRun" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="ToolbarLocation" Provider="Common.Settings.GenericSettingsProvider" Type="System.Windows.Controls.Dock" Scope="User">
<Setting Name="ToolbarLocation" Provider="CKaczor.GenericSettingsProvider.GenericSettingsProvider" Type="System.Windows.Controls.Dock" Scope="User">
<Value Profile="(Default)">Bottom</Value>
</Setting>
<Setting Name="OpenAllSleepInterval" Provider="Common.Settings.GenericSettingsProvider" Type="System.Int32" Scope="User">
<Setting Name="OpenAllSleepInterval" Provider="CKaczor.GenericSettingsProvider.GenericSettingsProvider" Type="System.Int32" Scope="User">
<Value Profile="(Default)">500</Value>
</Setting>
<Setting Name="Browser" Provider="Common.Settings.GenericSettingsProvider" Type="System.String" Scope="User">
<Setting Name="Browser" Provider="CKaczor.GenericSettingsProvider.GenericSettingsProvider" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="OpenAllSleepIntervalFirst" Provider="Common.Settings.GenericSettingsProvider" Type="System.Int32" Scope="User">
<Setting Name="OpenAllSleepIntervalFirst" Provider="CKaczor.GenericSettingsProvider.GenericSettingsProvider" Type="System.Int32" Scope="User">
<Value Profile="(Default)">1500</Value>
</Setting>
<Setting Name="MultipleLineDisplay" Provider="Common.Settings.GenericSettingsProvider" Type="FeedCenter.Options.MultipleLineDisplay" Scope="User">
<Setting Name="MultipleLineDisplay" Provider="CKaczor.GenericSettingsProvider.GenericSettingsProvider" Type="FeedCenter.Options.MultipleLineDisplay" Scope="User">
<Value Profile="(Default)">Normal</Value>
</Setting>
<Setting Name="VersionLocation" Type="System.String" Scope="Application">
@@ -68,8 +68,11 @@
<Setting Name="LastCategoryID" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="DefaultUserAgent" Provider="Common.Settings.GenericSettingsProvider" Type="System.String" Scope="User">
<Setting Name="DefaultUserAgent" Provider="CKaczor.GenericSettingsProvider.GenericSettingsProvider" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="DatabaseFile" Type="System.String" Scope="Application">
<Value Profile="(Default)">FeedCenter.realm</Value>
</Setting>
</Settings>
</SettingsFile>

View File

@@ -10,6 +10,11 @@ namespace FeedCenter
{
public static object OpenDataStore()
{
if (!Database.Exists)
return null;
Database.Load();
return Database.Entities;
}

View File

@@ -7,12 +7,10 @@ using System.ComponentModel;
using System.Threading;
using System.Windows.Threading;
namespace FeedCenter
{
namespace FeedCenter;
public partial class SplashWindow : IDisposable
{
#region Progress step
private class ProgressStep
{
public delegate bool ProgressCallback();
@@ -29,18 +27,10 @@ namespace FeedCenter
}
}
#endregion
#region Member variables
private readonly List<ProgressStep> _progressSteps = new();
private readonly Dispatcher _dispatcher;
private readonly BackgroundWorker _backgroundWorker;
#endregion
#region Constructor
public SplashWindow()
{
InitializeComponent();
@@ -48,46 +38,28 @@ namespace FeedCenter
// Store the dispatcher - the background worker has trouble getting the right thread when called from Main
_dispatcher = Dispatcher.CurrentDispatcher;
// Get the version to display
var version = UpdateCheck.LocalVersion.ToString();
VersionLabel.Content = string.Format(Properties.Resources.Version, UpdateCheck.LocalVersion.ToString());
// Show the version
VersionLabel.Content = string.Format(Properties.Resources.Version, version);
// Set the starting caption
StatusLabel.Content = Properties.Resources.SplashStarting;
// Build the progress steps
LoadProgressSteps();
// Set the progress bar to the number of steps
ProgressBar.Maximum = _progressSteps.Count;
// Create the worker with progress and cancel
_backgroundWorker = new BackgroundWorker { WorkerReportsProgress = true, WorkerSupportsCancellation = true };
// Setup the events
_backgroundWorker.DoWork += HandleBackgroundWorkerDoWork;
_backgroundWorker.ProgressChanged += HandleBackgroundWorkerProgressChanged;
_backgroundWorker.RunWorkerCompleted += HandleBackgroundWorkerCompleted;
}
#endregion
#region Form overrides
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
// Start the worker
_backgroundWorker.RunWorkerAsync();
}
#endregion
#region Background worker
private void HandleBackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (!_dispatcher.CheckAccess())
@@ -96,7 +68,6 @@ namespace FeedCenter
return;
}
// Update the progress bar
ProgressBar.Value += e.ProgressPercentage;
// Get the message
@@ -132,7 +103,7 @@ namespace FeedCenter
var result = progressStep.Callback();
// If the step indicated a skip then set the skip key, otherwise clear it
skipKey = (result ? string.Empty : progressStep.Key);
skipKey = result ? string.Empty : progressStep.Key;
}
// Stop if cancelled
@@ -162,67 +133,65 @@ namespace FeedCenter
// Move the progress bar to the max just in case
ProgressBar.Value = ProgressBar.Maximum;
// Close the window
Close();
}
#endregion
#region Progress steps
private static class ProgressKey
{
public const string DatabaseCreate = "CreateDatabase";
public const string DatabaseUpdate = "UpdateDatabase";
public const string DatabaseMaintenance = "MaintainDatabase";
public const string ManageLegacyDatabase = "ManageLegacyDatabase";
public const string ManageDatabase = "ManageDatabase";
}
private void LoadProgressSteps()
{
// Load the progress steps
_progressSteps.Add(new ProgressStep(ProgressKey.DatabaseCreate, Properties.Resources.SplashCheckingForDatabase, CheckDatabase));
_progressSteps.Add(new ProgressStep(ProgressKey.DatabaseCreate, Properties.Resources.SplashCreatingDatabase, CreateDatabase));
_progressSteps.Add(new ProgressStep(ProgressKey.ManageLegacyDatabase, Properties.Resources.SplashCheckingForLegacyDatabase, CheckDatabase));
_progressSteps.Add(new ProgressStep(ProgressKey.ManageLegacyDatabase, Properties.Resources.SplashUpdatingLegacyDatabase, UpdateDatabase));
_progressSteps.Add(new ProgressStep(ProgressKey.ManageLegacyDatabase, Properties.Resources.SplashMaintainingLegacyDatabase, MaintainDatabase));
_progressSteps.Add(new ProgressStep(ProgressKey.ManageLegacyDatabase, Properties.Resources.SplashMigratingLegacyDatabase, MigrateDatabase));
_progressSteps.Add(new ProgressStep(ProgressKey.DatabaseUpdate, Properties.Resources.SplashUpdatingDatabase, UpdateDatabase));
_progressSteps.Add(new ProgressStep(ProgressKey.DatabaseMaintenance, Properties.Resources.SplashMaintainingDatabase, MaintainDatabase));
_progressSteps.Add(new ProgressStep(ProgressKey.ManageDatabase, Properties.Resources.SplashLoadingDatabase, LoadDatabase));
}
private static bool CheckDatabase()
{
// If the database exists then we're done
return !Database.DatabaseExists;
}
private static bool CreateDatabase()
{
// Create the database
//Database.CreateDatabase();
return true;
return LegacyDatabase.Exists;
}
private static bool UpdateDatabase()
{
// Update the database
// Database.UpdateDatabase();
LegacyDatabase.UpdateDatabase();
return true;
}
private static bool MaintainDatabase()
{
// Maintain the database
//Database.MaintainDatabase();
LegacyDatabase.MaintainDatabase();
return true;
}
#endregion
private static bool MigrateDatabase()
{
LegacyDatabase.MigrateDatabase();
return true;
}
private bool LoadDatabase()
{
_dispatcher.Invoke(() =>
{
Database.Load();
Settings.Default.Reload();
});
return true;
}
public void Dispose()
{
_backgroundWorker?.Dispose();
}
}
}

View File

@@ -13,12 +13,6 @@
<setting name="WindowLocked" serializeAs="String">
<value>False</value>
</setting>
<setting name="WindowSize" serializeAs="String">
<value>0,0</value>
</setting>
<setting name="WindowLocation" serializeAs="String">
<value>0,0</value>
</setting>
<setting name="CheckVersionAtStartup" serializeAs="String">
<value>True</value>
</setting>
@@ -40,9 +34,6 @@
<setting name="FirstRun" serializeAs="String">
<value>True</value>
</setting>
<setting name="ToolbarLocation" serializeAs="String">
<value>Bottom</value>
</setting>
<setting name="OpenAllSleepInterval" serializeAs="String">
<value>500</value>
</setting>
@@ -52,9 +43,6 @@
<setting name="OpenAllSleepIntervalFirst" serializeAs="String">
<value>1500</value>
</setting>
<setting name="MultipleLineDisplay" serializeAs="String">
<value>Normal</value>
</setting>
<setting name="LastCategoryID" serializeAs="String">
<value />
</setting>
@@ -68,7 +56,7 @@
<setting name="LogDatabase" serializeAs="String">
<value>True</value>
</setting>
<setting name="DatabaseFile" serializeAs="String">
<setting name="DatabaseFile_Legacy" serializeAs="String">
<value>FeedCenter.sdf</value>
</setting>
<setting name="ProgressSleepInterval" serializeAs="String">
@@ -83,6 +71,9 @@
<setting name="VersionLocation" serializeAs="String">
<value>https://api.github.com/repos/ckaczor/FeedCenter/releases/latest</value>
</setting>
<setting name="DatabaseFile" serializeAs="String">
<value>FeedCenter.realm</value>
</setting>
</FeedCenter.Properties.Settings>
</applicationSettings>
</configuration>