1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Storage - Clean up legacy project and refactor repositories to use short lived DB contexts

This commit is contained in:
Robert 2024-03-10 21:18:20 +01:00
parent d4e4d52f84
commit 4bae9e89cf
105 changed files with 818 additions and 578 deletions

View File

@ -40,7 +40,7 @@
<PackageReference Include="EmbedIO" />
<PackageReference Include="HidSharp" />
<PackageReference Include="Humanizer.Core" />
<PackageReference Include="JetBrains.Annotations" />
<PackageReference Include="JetBrains.Annotations" PrivateAssets="All"/>
<PackageReference Include="McMaster.NETCore.Plugins" />
<PackageReference Include="RGB.NET.Core" />
<PackageReference Include="RGB.NET.Layout" />

View File

@ -91,7 +91,7 @@ public static class Constants
/// <summary>
/// The plugin used by core components of Artemis
/// </summary>
public static readonly Plugin CorePlugin = new(CorePluginInfo, new DirectoryInfo(ApplicationFolder), new PluginEntity(){Id = CorePluginInfo.Guid}, false);
public static readonly Plugin CorePlugin = new(CorePluginInfo, new DirectoryInfo(ApplicationFolder), new PluginEntity(){PluginGuid = CorePluginInfo.Guid}, false);
/// <summary>
/// A read-only collection containing all primitive numeric types

View File

@ -309,13 +309,7 @@ public class Plugin : CorePropertyChanged, IDisposable
{
FeatureRemoved?.Invoke(this, e);
}
internal void ApplyToEntity()
{
Entity.Id = Guid;
Entity.IsEnabled = IsEnabled;
}
internal void AddFeature(PluginFeatureInfo featureInfo)
{
if (featureInfo.Plugin != this)

View File

@ -94,7 +94,7 @@ public class PluginSetting<T> : CorePropertyChanged, IPluginSetting
return;
_pluginSettingEntity.Value = CoreJson.Serialize(Value);
_pluginRepository.SaveChanges();
_pluginRepository.SaveSetting(_pluginSettingEntity);
OnSettingSaved();
}

View File

@ -31,10 +31,13 @@ public class PluginSettings
/// Gets the setting with the provided name. If the setting does not exist yet, it is created.
/// </summary>
/// <typeparam name="T">The type of the setting, can be any serializable type</typeparam>
/// <param name="name">The name of the setting</param>
/// <param name="name">The name of the setting, may not be longer than 128 characters</param>
/// <param name="defaultValue">The default value to use if the setting does not exist yet</param>
public PluginSetting<T> GetSetting<T>(string name, T? defaultValue = default)
{
if (name.Length > 128)
throw new ArtemisCoreException("Setting name cannot be longer than 128 characters");
lock (_settingEntities)
{
// Return cached value if available
@ -51,7 +54,7 @@ public class PluginSettings
PluginGuid = Plugin.Guid,
Value = CoreJson.Serialize(defaultValue)
};
_pluginRepository.AddSetting(settingEntity);
_pluginRepository.SaveSetting(settingEntity);
}
PluginSetting<T> pluginSetting = new(_pluginRepository, settingEntity);

View File

@ -202,7 +202,7 @@ internal class DeviceService : IDeviceService
_enabledDevices.Add(device);
device.IsEnabled = true;
device.Save();
_deviceRepository.SaveChanges();
_deviceRepository.Save(device.DeviceEntity);
OnDeviceEnabled(new DeviceEventArgs(device));
UpdateLeds();
@ -217,7 +217,7 @@ internal class DeviceService : IDeviceService
_enabledDevices.Remove(device);
device.IsEnabled = false;
device.Save();
_deviceRepository.SaveChanges();
_deviceRepository.Save(device.DeviceEntity);
OnDeviceDisabled(new DeviceEventArgs(device));
UpdateLeds();
@ -227,7 +227,7 @@ internal class DeviceService : IDeviceService
public void SaveDevice(ArtemisDevice artemisDevice)
{
artemisDevice.Save();
_deviceRepository.SaveChanges();
_deviceRepository.Save(artemisDevice.DeviceEntity);
UpdateLeds();
}
@ -236,7 +236,7 @@ internal class DeviceService : IDeviceService
{
foreach (ArtemisDevice artemisDevice in _devices)
artemisDevice.Save();
_deviceRepository.SaveChanges();
_deviceRepository.SaveRange(_devices.Select(d => d.DeviceEntity));
UpdateLeds();
}

View File

@ -372,12 +372,12 @@ internal class PluginManagementService : IPluginManagementService
}
// Load the entity and fall back on creating a new one
PluginEntity? entity = _pluginRepository.GetPluginByGuid(pluginInfo.Guid);
PluginEntity? entity = _pluginRepository.GetPluginByPluginGuid(pluginInfo.Guid);
bool loadedFromStorage = entity != null;
if (entity == null)
{
entity = new PluginEntity {Id = pluginInfo.Guid};
_pluginRepository.AddPlugin(entity);
entity = new PluginEntity {PluginGuid = pluginInfo.Guid};
_pluginRepository.SavePlugin(entity);
}
Plugin plugin = new(pluginInfo, directory, entity, loadedFromStorage);
@ -815,7 +815,7 @@ internal class PluginManagementService : IPluginManagementService
plugin.Entity.Features.Add(featureInfo.Instance!.Entity);
}
_pluginRepository.SaveChanges();
_pluginRepository.SavePlugin(plugin.Entity);
}
#endregion

View File

@ -34,7 +34,7 @@ public interface ISettingsService : IProtectedArtemisService
/// Gets the setting with the provided name. If the setting does not exist yet, it is created.
/// </summary>
/// <typeparam name="T">The type of the setting, can be any serializable type</typeparam>
/// <param name="name">The name of the setting</param>
/// <param name="name">The name of the setting, may not be longer than 128 characters</param>
/// <param name="defaultValue">The default value to use if the setting does not exist yet</param>
/// <returns></returns>
PluginSetting<T> GetSetting<T>(string name, T? defaultValue = default);

View File

@ -54,13 +54,7 @@ public interface IProfileService : IArtemisService
/// </summary>
/// <param name="profileConfiguration">The profile configuration of the profile to activate.</param>
void DeactivateProfile(ProfileConfiguration profileConfiguration);
/// <summary>
/// Permanently deletes the profile of the given <see cref="ProfileConfiguration" />.
/// </summary>
/// <param name="profileConfiguration">The profile configuration of the profile to delete.</param>
void DeleteProfile(ProfileConfiguration profileConfiguration);
/// <summary>
/// Saves the provided <see cref="ProfileCategory" /> and it's <see cref="ProfileConfiguration" />s but not the
/// <see cref="Profile" />s themselves.

View File

@ -22,6 +22,7 @@ internal class ProfileService : IProfileService
{
private readonly ILogger _logger;
private readonly IProfileCategoryRepository _profileCategoryRepository;
private readonly IProfileRepository _profileRepository;
private readonly IPluginManagementService _pluginManagementService;
private readonly IDeviceService _deviceService;
private readonly List<ArtemisKeyboardKeyEventArgs> _pendingKeyboardEvents = new();
@ -34,6 +35,7 @@ internal class ProfileService : IProfileService
public ProfileService(ILogger logger,
IProfileCategoryRepository profileCategoryRepository,
IProfileRepository profileRepository,
IPluginManagementService pluginManagementService,
IInputService inputService,
IDeviceService deviceService,
@ -41,6 +43,7 @@ internal class ProfileService : IProfileService
{
_logger = logger;
_profileCategoryRepository = profileCategoryRepository;
_profileRepository = profileRepository;
_pluginManagementService = pluginManagementService;
_deviceService = deviceService;
_profileMigrators = profileMigrators;
@ -214,20 +217,7 @@ internal class ProfileService : IProfileService
profileConfiguration.Profile.ShouldDisplay = false;
}
/// <inheritdoc />
public void DeleteProfile(ProfileConfiguration profileConfiguration)
{
DeactivateProfile(profileConfiguration);
ProfileCategory category = profileConfiguration.Category;
category.RemoveProfileConfiguration(profileConfiguration);
category.Entity.ProfileConfigurations.Remove(profileConfiguration.Entity);
_profileCategoryRepository.SaveChanges();
}
/// <inheritdoc />
public ProfileCategory CreateProfileCategory(string name, bool addToTop = false)
{
@ -240,6 +230,8 @@ internal class ProfileService : IProfileService
category.Order++;
category.Save();
}
_profileCategoryRepository.SaveRange(ProfileCategories.Select(c => c.Entity).ToList());
}
else
{
@ -274,32 +266,32 @@ internal class ProfileService : IProfileService
SaveProfileCategory(category);
return configuration;
}
/// <inheritdoc />
public void RemoveProfileConfiguration(ProfileConfiguration profileConfiguration)
{
ProfileCategory category = profileConfiguration.Category;
category.RemoveProfileConfiguration(profileConfiguration);
DeactivateProfile(profileConfiguration);
SaveProfileCategory(profileConfiguration.Category);
profileConfiguration.Dispose();
ProfileCategory category = profileConfiguration.Category;
category.RemoveProfileConfiguration(profileConfiguration);
category.Save();
_profileRepository.Remove(profileConfiguration.Entity);
_profileCategoryRepository.Save(category.Entity);
}
/// <inheritdoc />
public void SaveProfileCategory(ProfileCategory profileCategory)
{
profileCategory.Save();
_profileCategoryRepository.SaveChanges();
_profileCategoryRepository.Save(profileCategory.Entity);
ProfileCategories = new ReadOnlyCollection<ProfileCategory>(ProfileCategories.OrderBy(c => c.Order).ToList());
}
/// <inheritdoc />
public void SaveProfile(Profile profile, bool includeChildren)
{
Stopwatch sw = new();
sw.Start();
_logger.Debug("Updating profile - Saving {Profile}", profile);
profile.Save();
if (includeChildren)
@ -312,7 +304,7 @@ internal class ProfileService : IProfileService
profile.IsFreshImport = false;
profile.ProfileEntity.IsFreshImport = false;
SaveProfileCategory(profile.Configuration.Category);
_profileRepository.Save(profile.Configuration.Entity);
// If the provided profile is external (cloned or from the workshop?) but it is loaded locally too, reload the local instance
// A bit dodge but it ensures local instances always represent the latest stored version
@ -320,17 +312,10 @@ internal class ProfileService : IProfileService
.SelectMany(c => c.ProfileConfigurations)
.FirstOrDefault(p => p.Profile != null && p.Profile != profile && p.ProfileId == profile.ProfileEntity.Id);
if (localInstance == null)
{
sw.Stop();
_logger.Debug("Updated profile - Saved {Profile} in {Time}ms", profile, sw.Elapsed.TotalMilliseconds);
return;
}
DeactivateProfile(localInstance);
ActivateProfile(localInstance);
sw.Stop();
_logger.Debug("Updated profile - Saved {Profile} in {Time}ms", profile, sw.Elapsed.TotalMilliseconds);
}
/// <inheritdoc />
@ -393,7 +378,7 @@ internal class ProfileService : IProfileService
JsonObject? profileJson = CoreJson.Deserialize<JsonObject>(await profileReader.ReadToEndAsync());
// Before deserializing, apply any pending migrations
MigrateProfile(configurationJson, profileJson);
_profileRepository.MigrateProfile(configurationJson, profileJson);
// Deserialize profile configuration to ProfileConfigurationEntity
ProfileConfigurationEntity? configurationEntity = configurationJson?.Deserialize<ProfileConfigurationEntity>(Constants.JsonConvertSettings);
@ -453,7 +438,7 @@ internal class ProfileService : IProfileService
{
ProfileConfiguration imported = await ImportProfile(archiveStream, profileConfiguration.Category, true, true, null, profileConfiguration.Order + 1);
DeleteProfile(profileConfiguration);
RemoveProfileConfiguration(profileConfiguration);
SaveProfileCategory(imported.Category);
return imported;
@ -479,24 +464,7 @@ internal class ProfileService : IProfileService
{
_pendingKeyboardEvents.Add(e);
}
private void MigrateProfile(JsonObject? configurationJson, JsonObject? profileJson)
{
if (configurationJson == null || profileJson == null)
return;
configurationJson["Version"] ??= 0;
foreach (IProfileMigration profileMigrator in _profileMigrators.OrderBy(m => m.Version))
{
if (profileMigrator.Version <= configurationJson["Version"]!.GetValue<int>())
continue;
profileMigrator.Migrate(configurationJson, profileJson);
configurationJson["Version"] = profileMigrator.Version;
}
}
/// <summary>
/// Populates all missing LEDs on all currently active profiles
/// </summary>

View File

@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
@ -13,8 +12,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="LiteDB" Version="5.0.17" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
<PackageReference Include="LiteDB" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@ -1,6 +1,6 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.General;
namespace Artemis.Storage.Legacy.Entities.General;
public class QueuedActionEntity
internal class QueuedActionEntity
{
public QueuedActionEntity()
{

View File

@ -1,6 +1,6 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.General;
namespace Artemis.Storage.Legacy.Entities.General;
public class ReleaseEntity
internal class ReleaseEntity
{
public Guid Id { get; set; }

View File

@ -1,6 +1,6 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.General;
namespace Artemis.Storage.Legacy.Entities.General;
public class ScriptConfigurationEntity
internal class ScriptConfigurationEntity
{
public Guid Id { get; set; }

View File

@ -1,9 +1,9 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Plugins;
namespace Artemis.Storage.Legacy.Entities.Plugins;
/// <summary>
/// Represents the configuration of a plugin, each plugin has one configuration
/// </summary>
public class PluginEntity
internal class PluginEntity
{
public PluginEntity()
{
@ -19,7 +19,7 @@ public class PluginEntity
{
return new Artemis.Storage.Entities.Plugins.PluginEntity()
{
Id = Id,
PluginGuid = Id,
IsEnabled = IsEnabled,
Features = Features.Select(f => f.Migrate()).ToList()
};
@ -29,7 +29,7 @@ public class PluginEntity
/// <summary>
/// Represents the configuration of a plugin feature, each feature has one configuration
/// </summary>
public class PluginFeatureEntity
internal class PluginFeatureEntity
{
public string Type { get; set; } = string.Empty;
public bool IsEnabled { get; set; }

View File

@ -1,9 +1,9 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Plugins;
namespace Artemis.Storage.Legacy.Entities.Plugins;
/// <summary>
/// Represents the setting of a plugin, a plugin can have multiple settings
/// </summary>
public class PluginSettingEntity
internal class PluginSettingEntity
{
public Guid Id { get; set; }
public Guid PluginGuid { get; set; }

View File

@ -1,8 +1,8 @@
using Artemis.Storage.Migrator.Legacy.Entities.Profile.Conditions;
using Artemis.Storage.Legacy.Entities.Profile.Conditions;
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Abstract;
namespace Artemis.Storage.Legacy.Entities.Profile.Abstract;
public abstract class RenderElementEntity
internal abstract class RenderElementEntity
{
public Guid Id { get; set; }
public Guid ParentId { get; set; }

View File

@ -1,6 +1,6 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.AdaptionHints;
namespace Artemis.Storage.Legacy.Entities.Profile.AdaptionHints;
public class CategoryAdaptionHintEntity : IAdaptionHintEntity
internal class CategoryAdaptionHintEntity : IAdaptionHintEntity
{
public int Category { get; set; }

View File

@ -1,6 +1,6 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.AdaptionHints;
namespace Artemis.Storage.Legacy.Entities.Profile.AdaptionHints;
public class DeviceAdaptionHintEntity : IAdaptionHintEntity
internal class DeviceAdaptionHintEntity : IAdaptionHintEntity
{
public int DeviceType { get; set; }

View File

@ -1,6 +1,6 @@
using System.Text.Json.Serialization;
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.AdaptionHints;
namespace Artemis.Storage.Legacy.Entities.Profile.AdaptionHints;
[JsonDerivedType(typeof(CategoryAdaptionHintEntity), "Category")]
[JsonDerivedType(typeof(DeviceAdaptionHintEntity), "Device")]

View File

@ -0,0 +1,6 @@
namespace Artemis.Storage.Legacy.Entities.Profile.AdaptionHints;
internal class KeyboardSectionAdaptionHintEntity : IAdaptionHintEntity
{
public int Section { get; set; }
}

View File

@ -1,6 +1,6 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.AdaptionHints;
namespace Artemis.Storage.Legacy.Entities.Profile.AdaptionHints;
public class SingleLedAdaptionHintEntity : IAdaptionHintEntity
internal class SingleLedAdaptionHintEntity : IAdaptionHintEntity
{
public int LedId { get; set; }

View File

@ -0,0 +1,3 @@
namespace Artemis.Storage.Legacy.Entities.Profile.Conditions;
internal class AlwaysOnConditionEntity : IConditionEntity;

View File

@ -1,8 +1,8 @@
using Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes;
using Artemis.Storage.Legacy.Entities.Profile.Nodes;
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Conditions;
namespace Artemis.Storage.Legacy.Entities.Profile.Conditions;
public class EventConditionEntity : IConditionEntity
internal class EventConditionEntity : IConditionEntity
{
public int TriggerMode { get; set; }
public int OverlapMode { get; set; }

View File

@ -1,6 +1,6 @@
using System.Text.Json.Serialization;
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Conditions;
namespace Artemis.Storage.Legacy.Entities.Profile.Conditions;
[JsonDerivedType(typeof(AlwaysOnConditionEntity), "AlwaysOn")]
[JsonDerivedType(typeof(EventConditionEntity), "Event")]

View File

@ -0,0 +1,3 @@
namespace Artemis.Storage.Legacy.Entities.Profile.Conditions;
internal class PlayOnceConditionEntity : IConditionEntity;

View File

@ -0,0 +1,10 @@
using Artemis.Storage.Legacy.Entities.Profile.Nodes;
namespace Artemis.Storage.Legacy.Entities.Profile.Conditions;
internal class StaticConditionEntity : IConditionEntity
{
public int PlayMode { get; set; }
public int StopMode { get; set; }
public NodeScriptEntity? Script { get; set; }
}

View File

@ -0,0 +1,9 @@
using Artemis.Storage.Legacy.Entities.Profile.Nodes;
namespace Artemis.Storage.Legacy.Entities.Profile.DataBindings;
internal class DataBindingEntity
{
public bool IsEnabled { get; set; }
public NodeScriptEntity? NodeScript { get; set; }
}

View File

@ -1,6 +1,6 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile;
namespace Artemis.Storage.Legacy.Entities.Profile;
public class DataModelPathEntity
internal class DataModelPathEntity
{
public string Path { get; set; } = string.Empty;
public string? DataModelId { get; set; }

View File

@ -1,9 +1,9 @@
using Artemis.Storage.Migrator.Legacy.Entities.Profile.Abstract;
using Artemis.Storage.Legacy.Entities.Profile.Abstract;
using LiteDB;
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile;
namespace Artemis.Storage.Legacy.Entities.Profile;
public class FolderEntity : RenderElementEntity
internal class FolderEntity : RenderElementEntity
{
public int Order { get; set; }
public string? Name { get; set; }

View File

@ -1,6 +1,6 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile;
namespace Artemis.Storage.Legacy.Entities.Profile;
public class KeyframeEntity
internal class KeyframeEntity
{
public TimeSpan Position { get; set; }
public int Timeline { get; set; }

View File

@ -1,6 +1,6 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile;
namespace Artemis.Storage.Legacy.Entities.Profile;
public class LayerBrushEntity
internal class LayerBrushEntity
{
public string ProviderId { get; set; } = string.Empty;
public string BrushType { get; set; } = string.Empty;

View File

@ -1,6 +1,6 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile;
namespace Artemis.Storage.Legacy.Entities.Profile;
public class LayerEffectEntity
internal class LayerEffectEntity
{
public string ProviderId { get; set; } = string.Empty;
public string EffectType { get; set; } = string.Empty;

View File

@ -1,10 +1,10 @@
using Artemis.Storage.Migrator.Legacy.Entities.Profile.Abstract;
using Artemis.Storage.Migrator.Legacy.Entities.Profile.AdaptionHints;
using Artemis.Storage.Legacy.Entities.Profile.Abstract;
using Artemis.Storage.Legacy.Entities.Profile.AdaptionHints;
using LiteDB;
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile;
namespace Artemis.Storage.Legacy.Entities.Profile;
public class LayerEntity : RenderElementEntity
internal class LayerEntity : RenderElementEntity
{
public LayerEntity()
{

View File

@ -1,6 +1,6 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile;
namespace Artemis.Storage.Legacy.Entities.Profile;
public class LedEntity
internal class LedEntity
{
public string LedName { get; set; } = string.Empty;
public string DeviceIdentifier { get; set; } = string.Empty;

View File

@ -1,6 +1,6 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes;
namespace Artemis.Storage.Legacy.Entities.Profile.Nodes;
public class NodeConnectionEntity
internal class NodeConnectionEntity
{
public NodeConnectionEntity()
{

View File

@ -1,6 +1,6 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes;
namespace Artemis.Storage.Legacy.Entities.Profile.Nodes;
public class NodeEntity
internal class NodeEntity
{
public NodeEntity()
{

View File

@ -1,6 +1,6 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes;
namespace Artemis.Storage.Legacy.Entities.Profile.Nodes;
public class NodePinCollectionEntity
internal class NodePinCollectionEntity
{
public NodePinCollectionEntity()
{

View File

@ -1,6 +1,6 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes;
namespace Artemis.Storage.Legacy.Entities.Profile.Nodes;
public class NodeScriptEntity
internal class NodeScriptEntity
{
public NodeScriptEntity()
{

View File

@ -3,9 +3,9 @@ using Artemis.Storage.Entities.Profile;
using LiteDB;
using Serilog;
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile;
namespace Artemis.Storage.Legacy.Entities.Profile;
public class ProfileCategoryEntity
internal class ProfileCategoryEntity
{
public Guid Id { get; set; }

View File

@ -1,8 +1,8 @@
using Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes;
using Artemis.Storage.Legacy.Entities.Profile.Nodes;
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile;
namespace Artemis.Storage.Legacy.Entities.Profile;
public class ProfileConfigurationEntity
internal class ProfileConfigurationEntity
{
public string Name { get; set; } = string.Empty;
public string? MaterialIcon { get; set; }

View File

@ -0,0 +1,7 @@
namespace Artemis.Storage.Legacy.Entities.Profile;
internal class ProfileConfigurationHotkeyEntity
{
public int? Key { get; set; }
public int? Modifiers { get; set; }
}

View File

@ -1,8 +1,8 @@
using Artemis.Storage.Migrator.Legacy.Entities.General;
using Artemis.Storage.Legacy.Entities.General;
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile;
namespace Artemis.Storage.Legacy.Entities.Profile;
public class ProfileEntity
internal class ProfileEntity
{
public ProfileEntity()
{

View File

@ -1,8 +1,8 @@
using Artemis.Storage.Migrator.Legacy.Entities.Profile.DataBindings;
using Artemis.Storage.Legacy.Entities.Profile.DataBindings;
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile;
namespace Artemis.Storage.Legacy.Entities.Profile;
public class PropertyEntity
internal class PropertyEntity
{
public string Identifier { get; set; } = string.Empty;
public string Value { get; set; } = string.Empty;

View File

@ -1,6 +1,6 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile;
namespace Artemis.Storage.Legacy.Entities.Profile;
public class PropertyGroupEntity
internal class PropertyGroupEntity
{
public string Identifier { get; set; } = string.Empty;
public List<PropertyEntity> Properties { get; set; } = new();

View File

@ -1,6 +1,6 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile;
namespace Artemis.Storage.Legacy.Entities.Profile;
public class TimelineEntity
internal class TimelineEntity
{
public TimeSpan StartSegmentLength { get; set; }
public TimeSpan MainSegmentLength { get; set; }

View File

@ -1,6 +1,6 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Surface;
namespace Artemis.Storage.Legacy.Entities.Surface;
public class DeviceEntity
internal class DeviceEntity
{
public DeviceEntity()
{
@ -65,13 +65,13 @@ public class DeviceEntity
}
}
public class InputMappingEntity
internal class InputMappingEntity
{
public int OriginalLedId { get; set; }
public int MappedLedId { get; set; }
}
public class DeviceInputIdentifierEntity
internal class DeviceInputIdentifierEntity
{
public string InputProvider { get; set; } = string.Empty;
public object Identifier { get; set; } = string.Empty;

View File

@ -1,6 +1,6 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Workshop;
namespace Artemis.Storage.Legacy.Entities.Workshop;
public class EntryEntity
internal class EntryEntity
{
public Guid Id { get; set; }

View File

@ -0,0 +1,163 @@
using Artemis.Core;
using Artemis.Storage.Legacy.Entities.General;
using Artemis.Storage.Legacy.Entities.Plugins;
using Artemis.Storage.Legacy.Entities.Profile;
using Artemis.Storage.Legacy.Entities.Surface;
using Artemis.Storage.Legacy.Entities.Workshop;
using Artemis.Storage.Legacy.Migrations;
using Artemis.Storage.Legacy.Migrations.Storage;
using DryIoc;
using LiteDB;
using Serilog;
namespace Artemis.Storage.Legacy;
public static class LegacyMigrationService
{
public static void MigrateToSqlite(IContainer container)
{
ILogger logger = container.Resolve<ILogger>();
// Before creating a DB context which is kinda expensive, check if there's anything to migrate
if (!File.Exists(Path.Combine(Constants.DataFolder, "database.db")))
{
logger.Information("No legacy database found, nothing to migrate");
return;
}
using ArtemisDbContext dbContext = container.Resolve<ArtemisDbContext>();
MigrateToSqlite(logger, dbContext);
}
public static void MigrateToSqlite(ILogger logger, ArtemisDbContext dbContext)
{
if (!File.Exists(Path.Combine(Constants.DataFolder, "database.db")))
{
logger.Information("No legacy database found, nothing to migrate");
return;
}
logger.Information("Migrating legacy database...");
try
{
// Copy the database before using it, we're going to make some modifications to it and we don't want to mess up the original
string databasePath = Path.Combine(Constants.DataFolder, "database.db");
string tempPath = Path.Combine(Constants.DataFolder, "temp.db");
File.Copy(databasePath, tempPath, true);
using LiteRepository repository = new($"FileName={tempPath}");
// Apply pending LiteDB migrations, this includes a migration that transforms namespaces to Artemis.Storage.Legacy
ApplyPendingMigrations(logger, repository);
// Devices
if (!dbContext.Devices.Any())
{
logger.Information("Migrating devices");
List<DeviceEntity> legacyDevices = repository.Query<DeviceEntity>().Include(s => s.InputIdentifiers).ToList();
dbContext.Devices.AddRange(legacyDevices.Select(l => l.Migrate()));
dbContext.SaveChanges();
}
// Entries
if (!dbContext.Entries.Any())
{
logger.Information("Migrating entries");
List<EntryEntity> legacyEntries = repository.Query<EntryEntity>().ToList();
dbContext.Entries.AddRange(legacyEntries.Select(l => l.Migrate()));
dbContext.SaveChanges();
}
// Plugins
if (!dbContext.Plugins.Any())
{
logger.Information("Migrating plugins");
List<PluginEntity> legacyPlugins = repository.Query<PluginEntity>().ToList();
dbContext.Plugins.AddRange(legacyPlugins.Select(l => l.Migrate()));
dbContext.SaveChanges();
}
// PluginSettings
if (!dbContext.PluginSettings.Any())
{
logger.Information("Migrating plugin settings");
List<PluginSettingEntity> legacyPluginSettings = repository.Query<PluginSettingEntity>().ToList();
dbContext.PluginSettings.AddRange(legacyPluginSettings.Select(l => l.Migrate()));
dbContext.SaveChanges();
}
// ProfileCategories
if (!dbContext.ProfileCategories.Any())
{
logger.Information("Migrating profile categories");
List<ProfileCategoryEntity> legacyProfileCategories = repository.Query<ProfileCategoryEntity>().ToList();
ILiteStorage<Guid> profileIcons = repository.Database.GetStorage<Guid>("profileIcons");
List<ProfileEntity> legacyProfiles = repository.Query<ProfileEntity>().ToList();
dbContext.ProfileCategories.AddRange(legacyProfileCategories.Select(l => l.Migrate(logger, legacyProfiles, profileIcons)));
dbContext.SaveChanges();
}
// Releases
if (!dbContext.Releases.Any())
{
logger.Information("Migrating releases");
List<ReleaseEntity> legacyReleases = repository.Query<ReleaseEntity>().ToList();
dbContext.Releases.AddRange(legacyReleases.Select(l => l.Migrate()));
dbContext.SaveChanges();
}
// After a successful migration, keep the legacy database around for a while
File.Move(Path.Combine(Constants.DataFolder, "database.db"), Path.Combine(Constants.DataFolder, "legacy.db"));
logger.Information("Legacy database migrated");
}
catch (Exception e)
{
logger.Error(e, "Failed to migrate legacy database");
throw;
}
finally
{
File.Delete(Path.Combine(Constants.DataFolder, "temp.db"));
}
}
private static void ApplyPendingMigrations(ILogger logger, LiteRepository repository)
{
List<IStorageMigration> migrations =
[
new M0020AvaloniaReset(),
new M0021GradientNodes(),
new M0022TransitionNodes(),
new M0023LayoutProviders(),
new M0024NodeProviders(),
new M0025NodeProvidersProfileConfig(),
new M0026NodeStorage(logger),
new M0027Namespace()
];
foreach (IStorageMigration storageMigration in migrations.OrderBy(m => m.UserVersion))
{
if (repository.Database.UserVersion >= storageMigration.UserVersion)
continue;
logger.Information("Applying storage migration {storageMigration} to update DB from v{oldVersion} to v{newVersion}",
storageMigration.GetType().Name, repository.Database.UserVersion, storageMigration.UserVersion);
repository.Database.BeginTrans();
try
{
storageMigration.Apply(repository);
}
catch (Exception)
{
repository.Database.Rollback();
throw;
}
repository.Database.Commit();
repository.Database.UserVersion = storageMigration.UserVersion;
}
}
}

View File

@ -1,6 +1,6 @@
using System.Text.Json.Nodes;
namespace Artemis.Storage.Migrator.Legacy.Migrations;
namespace Artemis.Storage.Legacy.Migrations;
public interface IProfileMigration
{

View File

@ -1,6 +1,6 @@
using LiteDB;
namespace Artemis.Storage.Migrator.Legacy.Migrations;
namespace Artemis.Storage.Legacy.Migrations;
public interface IStorageMigration
{

View File

@ -1,8 +1,8 @@
using LiteDB;
namespace Artemis.Storage.Migrator.Legacy.Migrations.Storage;
namespace Artemis.Storage.Legacy.Migrations.Storage;
public class M0020AvaloniaReset : IStorageMigration
internal class M0020AvaloniaReset : IStorageMigration
{
public int UserVersion => 20;

View File

@ -1,10 +1,10 @@
using Artemis.Storage.Migrator.Legacy.Entities.Profile;
using Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes;
using Artemis.Storage.Legacy.Entities.Profile;
using Artemis.Storage.Legacy.Entities.Profile.Nodes;
using LiteDB;
namespace Artemis.Storage.Migrator.Legacy.Migrations.Storage;
namespace Artemis.Storage.Legacy.Migrations.Storage;
public class M0021GradientNodes : IStorageMigration
internal class M0021GradientNodes : IStorageMigration
{
private void MigrateDataBinding(PropertyEntity property)
{
@ -59,7 +59,7 @@ public class M0021GradientNodes : IStorageMigration
{
if (propertyGroup == null)
return;
foreach (PropertyGroupEntity propertyGroupPropertyGroup in propertyGroup.PropertyGroups)
MigrateDataBinding(propertyGroupPropertyGroup);

View File

@ -1,11 +1,11 @@
using Artemis.Storage.Migrator.Legacy.Entities.Profile;
using Artemis.Storage.Migrator.Legacy.Entities.Profile.Conditions;
using Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes;
using Artemis.Storage.Legacy.Entities.Profile;
using Artemis.Storage.Legacy.Entities.Profile.Conditions;
using Artemis.Storage.Legacy.Entities.Profile.Nodes;
using LiteDB;
namespace Artemis.Storage.Migrator.Legacy.Migrations.Storage;
namespace Artemis.Storage.Legacy.Migrations.Storage;
public class M0022TransitionNodes : IStorageMigration
internal class M0022TransitionNodes : IStorageMigration
{
private void MigrateNodeScript(NodeScriptEntity? nodeScript)
{

View File

@ -1,8 +1,8 @@
using LiteDB;
namespace Artemis.Storage.Migrator.Legacy.Migrations.Storage;
namespace Artemis.Storage.Legacy.Migrations.Storage;
public class M0023LayoutProviders : IStorageMigration
internal class M0023LayoutProviders : IStorageMigration
{
public int UserVersion => 23;
@ -19,9 +19,13 @@ public class M0023LayoutProviders : IStorageMigration
bsonDocument.Add("LayoutParameter", new BsonValue(customLayoutPath.AsString));
}
else if (bsonDocument.TryGetValue("DisableDefaultLayout", out BsonValue disableDefaultLayout) && disableDefaultLayout.AsBoolean)
{
bsonDocument.Add("LayoutType", new BsonValue("None"));
}
else
{
bsonDocument.Add("LayoutType", new BsonValue("Default"));
}
bsonDocument.Remove("CustomLayoutPath");
bsonDocument.Remove("DisableDefaultLayout");

View File

@ -1,8 +1,8 @@
using LiteDB;
namespace Artemis.Storage.Migrator.Legacy.Migrations.Storage;
namespace Artemis.Storage.Legacy.Migrations.Storage;
public class M0024NodeProviders : IStorageMigration
internal class M0024NodeProviders : IStorageMigration
{
public int UserVersion => 24;
@ -20,6 +20,7 @@ public class M0024NodeProviders : IStorageMigration
categoriesToUpdate.Add(profileCategoryBson);
}
}
categoryCollection.Update(categoriesToUpdate);
ILiteCollection<BsonDocument> collection = repository.Database.GetCollection("ProfileEntity");
@ -28,15 +29,12 @@ public class M0024NodeProviders : IStorageMigration
{
BsonArray? folders = profileBson["Folders"]?.AsArray;
BsonArray? layers = profileBson["Layers"]?.AsArray;
if (folders != null)
{
foreach (BsonValue folder in folders)
MigrateProfileElement(folder.AsDocument);
}
if (layers != null)
{
foreach (BsonValue layer in layers)
{
MigrateProfileElement(layer.AsDocument);
@ -44,8 +42,7 @@ public class M0024NodeProviders : IStorageMigration
MigratePropertyGroup(layer.AsDocument["TransformPropertyGroup"].AsDocument);
MigratePropertyGroup(layer.AsDocument["LayerBrush"]?["PropertyGroup"].AsDocument);
}
}
profilesToUpdate.Add(profileBson);
}
@ -56,10 +53,8 @@ public class M0024NodeProviders : IStorageMigration
{
BsonArray? layerEffects = profileElement["LayerEffects"]?.AsArray;
if (layerEffects != null)
{
foreach (BsonValue layerEffect in layerEffects)
MigratePropertyGroup(layerEffect.AsDocument["PropertyGroup"].AsDocument);
}
BsonValue? displayCondition = profileElement["DisplayCondition"];
if (displayCondition != null)
@ -75,16 +70,12 @@ public class M0024NodeProviders : IStorageMigration
BsonArray? propertyGroups = propertyGroup["PropertyGroups"]?.AsArray;
if (properties != null)
{
foreach (BsonValue property in properties)
MigrateNodeScript(property.AsDocument["DataBinding"]?["NodeScript"]?.AsDocument);
}
if (propertyGroups != null)
{
foreach (BsonValue childPropertyGroup in propertyGroups)
MigratePropertyGroup(childPropertyGroup.AsDocument);
}
}
private void MigrateNodeScript(BsonDocument? nodeScript)

View File

@ -1,8 +1,8 @@
using LiteDB;
namespace Artemis.Storage.Migrator.Legacy.Migrations.Storage;
namespace Artemis.Storage.Legacy.Migrations.Storage;
public class M0025NodeProvidersProfileConfig : IStorageMigration
internal class M0025NodeProvidersProfileConfig : IStorageMigration
{
public int UserVersion => 25;
@ -20,13 +20,14 @@ public class M0025NodeProvidersProfileConfig : IStorageMigration
profile["Version"] = 2;
MigrateNodeScript(profile["ActivationCondition"]?.AsDocument);
}
toUpdate.Add(profileCategoryBson);
}
}
categoryCollection.Update(toUpdate);
}
private void MigrateNodeScript(BsonDocument? nodeScript)
{
if (nodeScript == null || nodeScript.Keys.Count == 0)

View File

@ -3,9 +3,9 @@ using System.Text.Json.Nodes;
using LiteDB;
using Serilog;
namespace Artemis.Storage.Migrator.Legacy.Migrations.Storage;
namespace Artemis.Storage.Legacy.Migrations.Storage;
public class M0026NodeStorage : IStorageMigration
internal class M0026NodeStorage : IStorageMigration
{
private readonly ILogger _logger;
@ -45,13 +45,10 @@ public class M0026NodeStorage : IStorageMigration
BsonArray? layers = profileBson["Layers"]?.AsArray;
if (folders != null)
{
foreach (BsonValue folder in folders)
MigrateProfileElement(folder.AsDocument);
}
if (layers != null)
{
foreach (BsonValue layer in layers)
{
MigrateProfileElement(layer.AsDocument);
@ -59,7 +56,6 @@ public class M0026NodeStorage : IStorageMigration
MigratePropertyGroup(layer.AsDocument["TransformPropertyGroup"].AsDocument);
MigratePropertyGroup(layer.AsDocument["LayerBrush"]?["PropertyGroup"].AsDocument);
}
}
profilesToUpdate.Add(profileBson);
}
@ -71,10 +67,8 @@ public class M0026NodeStorage : IStorageMigration
{
BsonArray? layerEffects = profileElement["LayerEffects"]?.AsArray;
if (layerEffects != null)
{
foreach (BsonValue layerEffect in layerEffects)
MigratePropertyGroup(layerEffect.AsDocument["PropertyGroup"].AsDocument);
}
BsonValue? displayCondition = profileElement["DisplayCondition"];
if (displayCondition != null)
@ -90,16 +84,12 @@ public class M0026NodeStorage : IStorageMigration
BsonArray? propertyGroups = propertyGroup["PropertyGroups"]?.AsArray;
if (properties != null)
{
foreach (BsonValue property in properties)
MigrateNodeScript(property.AsDocument["DataBinding"]?["NodeScript"]?.AsDocument);
}
if (propertyGroups != null)
{
foreach (BsonValue childPropertyGroup in propertyGroups)
MigratePropertyGroup(childPropertyGroup.AsDocument);
}
}
private void MigrateNodeScript(BsonDocument? nodeScript)
@ -112,10 +102,8 @@ public class M0026NodeStorage : IStorageMigration
return;
foreach (BsonValue node in nodes)
{
// Migrate the storage of the node
node["Storage"] = MigrateNodeStorageJson(node.AsDocument["Storage"]?.AsString, _logger);
}
}
private static string? MigrateNodeStorageJson(string? json, ILogger logger)

View File

@ -1,8 +1,8 @@
using LiteDB;
namespace Artemis.Storage.Migrator.Legacy.Migrations.Storage;
namespace Artemis.Storage.Legacy.Migrations.Storage;
public class M0027Namespace : IStorageMigration
internal class M0027Namespace : IStorageMigration
{
public int UserVersion => 27;
@ -25,21 +25,17 @@ public class M0027Namespace : IStorageMigration
foreach ((string? key, BsonValue? value) in document)
{
if (key == "_type")
{
document[key] = document[key].AsString
.Replace("Artemis.Storage.Entities.Profile", "Artemis.Storage.Migrator.Legacy.Entities.Profile")
.Replace(", Artemis.Storage", ", Artemis.Storage.Migrator");
}
.Replace("Artemis.Storage.Entities", "Artemis.Storage.Legacy.Entities")
.Replace(", Artemis.Storage", ", Artemis.Storage.Legacy");
else if (value.IsDocument)
MigrateDocument(value.AsDocument);
else if (value.IsArray)
{
foreach (BsonValue bsonValue in value.AsArray)
{
if (bsonValue.IsDocument)
MigrateDocument(bsonValue.AsDocument);
}
}
}
}
}

View File

@ -0,0 +1,22 @@
// using Artemis.Core.DryIoc;
// using Artemis.Storage;
// using Artemis.Storage.Legacy;
// using DryIoc;
// using Microsoft.EntityFrameworkCore;
// using Serilog;
//
// using Container container = new(rules => rules
// .WithMicrosoftDependencyInjectionRules()
// .WithConcreteTypeDynamicRegistrations()
// .WithoutThrowOnRegisteringDisposableTransient());
//
// container.RegisterCore();
//
// ILogger logger = container.Resolve<ILogger>();
// ArtemisDbContext dbContext = container.Resolve<ArtemisDbContext>();
//
// logger.Information("Applying pending migrations...");
// dbContext.Database.Migrate();
// logger.Information("Pending migrations applied");
//
// MigrationService.MigrateToSqlite(logger, dbContext);

View File

@ -1,6 +0,0 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.AdaptionHints;
public class KeyboardSectionAdaptionHintEntity : IAdaptionHintEntity
{
public int Section { get; set; }
}

View File

@ -1,3 +0,0 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Conditions;
public class AlwaysOnConditionEntity : IConditionEntity;

View File

@ -1,3 +0,0 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Conditions;
public class PlayOnceConditionEntity : IConditionEntity;

View File

@ -1,10 +0,0 @@
using Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes;
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.Conditions;
public class StaticConditionEntity : IConditionEntity
{
public int PlayMode { get; set; }
public int StopMode { get; set; }
public NodeScriptEntity? Script { get; set; }
}

View File

@ -1,9 +0,0 @@
using Artemis.Storage.Migrator.Legacy.Entities.Profile.Nodes;
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile.DataBindings;
public class DataBindingEntity
{
public bool IsEnabled { get; set; }
public NodeScriptEntity? NodeScript { get; set; }
}

View File

@ -1,7 +0,0 @@
namespace Artemis.Storage.Migrator.Legacy.Entities.Profile;
public class ProfileConfigurationHotkeyEntity
{
public int? Key { get; set; }
public int? Modifiers { get; set; }
}

View File

@ -1,34 +0,0 @@
using Artemis.Storage.Migrator.Legacy.Migrations;
using LiteDB;
using Serilog;
namespace Artemis.Storage.Migrator.Legacy;
public static class StorageMigrationService
{
public static void ApplyPendingMigrations(ILogger logger, LiteRepository repository, IList<IStorageMigration> migrations)
{
foreach (IStorageMigration storageMigration in migrations.OrderBy(m => m.UserVersion))
{
if (repository.Database.UserVersion >= storageMigration.UserVersion)
continue;
logger.Information("Applying storage migration {storageMigration} to update DB from v{oldVersion} to v{newVersion}",
storageMigration.GetType().Name, repository.Database.UserVersion, storageMigration.UserVersion);
repository.Database.BeginTrans();
try
{
storageMigration.Apply(repository);
}
catch (Exception)
{
repository.Database.Rollback();
throw;
}
repository.Database.Commit();
repository.Database.UserVersion = storageMigration.UserVersion;
}
}
}

View File

@ -1,142 +0,0 @@
using Artemis.Core;
using Artemis.Core.DryIoc;
using Artemis.Storage.Migrator.Legacy;
using Artemis.Storage.Migrator.Legacy.Entities.General;
using Artemis.Storage.Migrator.Legacy.Entities.Plugins;
using Artemis.Storage.Migrator.Legacy.Entities.Profile;
using Artemis.Storage.Migrator.Legacy.Entities.Surface;
using Artemis.Storage.Migrator.Legacy.Entities.Workshop;
using Artemis.Storage.Migrator.Legacy.Migrations.Storage;
using DryIoc;
using LiteDB;
using Microsoft.EntityFrameworkCore;
using Serilog;
namespace Artemis.Storage.Migrator;
class Program
{
static void Main(string[] args)
{
using Container container = new(rules => rules
.WithMicrosoftDependencyInjectionRules()
.WithConcreteTypeDynamicRegistrations()
.WithoutThrowOnRegisteringDisposableTransient());
container.RegisterCore();
ILogger logger = container.Resolve<ILogger>();
ArtemisDbContext dbContext = container.Resolve<ArtemisDbContext>();
logger.Information("Applying pending migrations...");
dbContext.Database.Migrate();
logger.Information("Pending migrations applied");
if (!File.Exists(Path.Combine(Constants.DataFolder, "database.db")))
{
logger.Information("No legacy database found, nothing to migrate");
return;
}
logger.Information("Migrating legacy database...");
try
{
MigrateLegacyDatabase(logger, dbContext);
// After a successful migration, keep the legacy database around for a while
File.Move(Path.Combine(Constants.DataFolder, "database.db"), Path.Combine(Constants.DataFolder, "legacy.db"));
}
catch (Exception e)
{
logger.Error(e, "Failed to migrate legacy database");
throw;
}
finally
{
File.Delete(Path.Combine(Constants.DataFolder, "temp.db"));
}
logger.Information("Legacy database migrated");
}
private static void MigrateLegacyDatabase(ILogger logger, ArtemisDbContext dbContext)
{
// Copy the database before using it, we're going to make some modifications to it and we don't want to mess up the original
string databasePath = Path.Combine(Constants.DataFolder, "database.db");
string tempPath = Path.Combine(Constants.DataFolder, "temp.db");
File.Copy(databasePath, tempPath, true);
using LiteRepository repository = new($"FileName={tempPath}");
// Apply pending LiteDB migrations, this includes a migration that transforms namespaces to Artemis.Storage.Migrator
StorageMigrationService.ApplyPendingMigrations(
logger,
repository,
[
new M0020AvaloniaReset(),
new M0021GradientNodes(),
new M0022TransitionNodes(),
new M0023LayoutProviders(),
new M0024NodeProviders(),
new M0025NodeProvidersProfileConfig(),
new M0026NodeStorage(logger),
new M0027Namespace(),
]
);
// Devices
if (!dbContext.Devices.Any())
{
logger.Information("Migrating devices");
List<DeviceEntity> legacyDevices = repository.Query<DeviceEntity>().Include(s => s.InputIdentifiers).ToList();
dbContext.Devices.AddRange(legacyDevices.Select(l => l.Migrate()));
dbContext.SaveChanges();
}
// Entries
if (!dbContext.Entries.Any())
{
logger.Information("Migrating entries");
List<EntryEntity> legacyEntries = repository.Query<EntryEntity>().ToList();
dbContext.Entries.AddRange(legacyEntries.Select(l => l.Migrate()));
dbContext.SaveChanges();
}
// Plugins
if (!dbContext.Plugins.Any())
{
logger.Information("Migrating plugins");
List<PluginEntity> legacyPlugins = repository.Query<PluginEntity>().ToList();
dbContext.Plugins.AddRange(legacyPlugins.Select(l => l.Migrate()));
dbContext.SaveChanges();
}
// PluginSettings
if (!dbContext.PluginSettings.Any())
{
logger.Information("Migrating plugin settings");
List<PluginSettingEntity> legacyPluginSettings = repository.Query<PluginSettingEntity>().ToList();
dbContext.PluginSettings.AddRange(legacyPluginSettings.Select(l => l.Migrate()));
dbContext.SaveChanges();
}
// ProfileCategories
if (!dbContext.ProfileCategories.Any())
{
logger.Information("Migrating profile categories");
List<ProfileCategoryEntity> legacyProfileCategories = repository.Query<ProfileCategoryEntity>().ToList();
ILiteStorage<Guid> profileIcons = repository.Database.GetStorage<Guid>("profileIcons");
List<ProfileEntity> legacyProfiles = repository.Query<ProfileEntity>().ToList();
dbContext.ProfileCategories.AddRange(legacyProfileCategories.Select(l => l.Migrate(logger, legacyProfiles, profileIcons)));
dbContext.SaveChanges();
}
// Releases
if (!dbContext.Releases.Any())
{
logger.Information("Migrating releases");
List<ReleaseEntity> legacyReleases = repository.Query<ReleaseEntity>().ToList();
dbContext.Releases.AddRange(legacyReleases.Select(l => l.Migrate()));
dbContext.SaveChanges();
}
}
}

View File

@ -15,8 +15,10 @@ public class ArtemisDbContext : DbContext
public DbSet<DeviceEntity> Devices => Set<DeviceEntity>();
public DbSet<EntryEntity> Entries => Set<EntryEntity>();
public DbSet<PluginEntity> Plugins => Set<PluginEntity>();
public DbSet<PluginFeatureEntity> PluginFeatures => Set<PluginFeatureEntity>();
public DbSet<PluginSettingEntity> PluginSettings => Set<PluginSettingEntity>();
public DbSet<ProfileCategoryEntity> ProfileCategories => Set<ProfileCategoryEntity>();
public DbSet<ProfileContainerEntity> ProfileContainers => Set<ProfileContainerEntity>();
public DbSet<ReleaseEntity> Releases => Set<ReleaseEntity>();
public string DataFolder { get; set; } = string.Empty;

View File

@ -1,11 +1,17 @@
using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
namespace Artemis.Storage.Entities.General;
[Index(nameof(Version), IsUnique = true)]
[Index(nameof(InstalledAt))]
public class ReleaseEntity
{
public Guid Id { get; set; }
[MaxLength(64)]
public string Version { get; set; } = string.Empty;
public DateTimeOffset? InstalledAt { get; set; }
}

View File

@ -1,11 +1,14 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Microsoft.EntityFrameworkCore;
namespace Artemis.Storage.Entities.Plugins;
/// <summary>
/// Represents the configuration of a plugin, each plugin has one configuration
/// </summary>
[Index(nameof(PluginGuid), IsUnique = true)]
public class PluginEntity
{
public PluginEntity()
@ -14,6 +17,7 @@ public class PluginEntity
}
public Guid Id { get; set; }
public Guid PluginGuid { get; set; }
public bool IsEnabled { get; set; }
public List<PluginFeatureEntity> Features { get; set; }
@ -25,7 +29,7 @@ public class PluginEntity
public class PluginFeatureEntity
{
public Guid Id { get; set; }
public string Type { get; set; } = string.Empty;
public bool IsEnabled { get; set; }
}

View File

@ -1,15 +1,20 @@
using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
namespace Artemis.Storage.Entities.Plugins;
/// <summary>
/// Represents the setting of a plugin, a plugin can have multiple settings
/// </summary>
[Index(nameof(Name), nameof(PluginGuid), IsUnique = true)]
[Index(nameof(PluginGuid))]
public class PluginSettingEntity
{
public Guid Id { get; set; }
public Guid PluginGuid { get; set; }
[MaxLength(128)]
public string Name { get; set; } = string.Empty;
public string Value { get; set; } = string.Empty;
}

View File

@ -1,12 +1,16 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
namespace Artemis.Storage.Entities.Profile;
[Index(nameof(Name), IsUnique = true)]
public class ProfileCategoryEntity
{
public Guid Id { get; set; }
[MaxLength(64)]
public string Name { get; set; } = string.Empty;
public bool IsCollapsed { get; set; }
public bool IsSuspended { get; set; }

View File

@ -0,0 +1,10 @@
using System;
namespace Artemis.Storage.Entities;
internal class RawProfileContainer
{
public Guid Id { get; set; }
public string ProfileConfiguration { get; set; }
public string Profile { get; set; }
}

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Artemis.Storage.Entities.Surface;
@ -11,8 +12,12 @@ public class DeviceEntity
Categories = new List<int>();
}
[MaxLength(512)]
public string Id { get; set; } = string.Empty;
[MaxLength(512)]
public string DeviceProvider { get; set; } = string.Empty;
public float X { get; set; }
public float Y { get; set; }
public float Rotation { get; set; }
@ -22,10 +27,16 @@ public class DeviceEntity
public float GreenScale { get; set; }
public float BlueScale { get; set; }
public bool IsEnabled { get; set; }
public int PhysicalLayout { get; set; }
[MaxLength(32)]
public string? LogicalLayout { get; set; }
[MaxLength(64)]
public string? LayoutType { get; set; }
[MaxLength(512)]
public string? LayoutParameter { get; set; }
public List<DeviceInputIdentifierEntity> InputIdentifiers { get; set; }

View File

@ -1,22 +1,23 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Nodes;
using Microsoft.EntityFrameworkCore;
namespace Artemis.Storage.Entities.Workshop;
[Index(nameof(EntryId), IsUnique = true)]
public class EntryEntity
{
public Guid Id { get; set; }
public long EntryId { get; set; }
public int EntryType { get; set; }
public string Author { get; set; } = string.Empty;
public string Author { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public long ReleaseId { get; set; }
public string ReleaseVersion { get; set; } = string.Empty;
public DateTimeOffset InstalledAt { get; set; }
public Dictionary<string, object> Metadata { get; set; }
public Dictionary<string, object>? Metadata { get; set; }
}

View File

@ -0,0 +1,14 @@
using System;
namespace Artemis.Storage.Exceptions;
public class ArtemisStorageException : Exception
{
public ArtemisStorageException(string message) : base(message)
{
}
public ArtemisStorageException(string message, Exception innerException) : base(message, innerException)
{
}
}

View File

@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Artemis.Storage.Migrations
{
[DbContext(typeof(ArtemisDbContext))]
[Migration("20240308203921_Initial")]
[Migration("20240310201706_Initial")]
partial class Initial
{
/// <inheritdoc />
@ -31,10 +31,16 @@ namespace Artemis.Storage.Migrations
b.Property<string>("Version")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("InstalledAt");
b.HasIndex("Version")
.IsUnique();
b.ToTable("Releases");
});
@ -47,8 +53,14 @@ namespace Artemis.Storage.Migrations
b.Property<bool>("IsEnabled")
.HasColumnType("INTEGER");
b.Property<Guid>("PluginGuid")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("PluginGuid")
.IsUnique();
b.ToTable("Plugins");
});
@ -72,7 +84,7 @@ namespace Artemis.Storage.Migrations
b.HasIndex("PluginEntityId");
b.ToTable("PluginFeatureEntity");
b.ToTable("PluginFeatures");
});
modelBuilder.Entity("Artemis.Storage.Entities.Plugins.PluginSettingEntity", b =>
@ -83,6 +95,7 @@ namespace Artemis.Storage.Migrations
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<Guid>("PluginGuid")
@ -94,6 +107,11 @@ namespace Artemis.Storage.Migrations
b.HasKey("Id");
b.HasIndex("PluginGuid");
b.HasIndex("Name", "PluginGuid")
.IsUnique();
b.ToTable("PluginSettings");
});
@ -111,6 +129,7 @@ namespace Artemis.Storage.Migrations
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("TEXT");
b.Property<int>("Order")
@ -118,6 +137,9 @@ namespace Artemis.Storage.Migrations
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("ProfileCategories");
});
@ -146,12 +168,13 @@ namespace Artemis.Storage.Migrations
b.HasIndex("ProfileCategoryId");
b.ToTable("ProfileContainerEntity");
b.ToTable("ProfileContainers");
});
modelBuilder.Entity("Artemis.Storage.Entities.Surface.DeviceEntity", b =>
{
b.Property<string>("Id")
.HasMaxLength(512)
.HasColumnType("TEXT");
b.Property<float>("BlueScale")
@ -163,6 +186,7 @@ namespace Artemis.Storage.Migrations
b.Property<string>("DeviceProvider")
.IsRequired()
.HasMaxLength(512)
.HasColumnType("TEXT");
b.Property<float>("GreenScale")
@ -172,12 +196,15 @@ namespace Artemis.Storage.Migrations
.HasColumnType("INTEGER");
b.Property<string>("LayoutParameter")
.HasMaxLength(512)
.HasColumnType("TEXT");
b.Property<string>("LayoutType")
.HasMaxLength(64)
.HasColumnType("TEXT");
b.Property<string>("LogicalLayout")
.HasMaxLength(32)
.HasColumnType("TEXT");
b.Property<int>("PhysicalLayout")
@ -226,7 +253,6 @@ namespace Artemis.Storage.Migrations
.HasColumnType("TEXT");
b.Property<string>("Metadata")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Name")
@ -242,6 +268,9 @@ namespace Artemis.Storage.Migrations
b.HasKey("Id");
b.HasIndex("EntryId")
.IsUnique();
b.ToTable("Entries");
});

View File

@ -15,8 +15,8 @@ namespace Artemis.Storage.Migrations
name: "Devices",
columns: table => new
{
Id = table.Column<string>(type: "TEXT", nullable: false),
DeviceProvider = table.Column<string>(type: "TEXT", nullable: false),
Id = table.Column<string>(type: "TEXT", maxLength: 512, nullable: false),
DeviceProvider = table.Column<string>(type: "TEXT", maxLength: 512, nullable: false),
X = table.Column<float>(type: "REAL", nullable: false),
Y = table.Column<float>(type: "REAL", nullable: false),
Rotation = table.Column<float>(type: "REAL", nullable: false),
@ -27,9 +27,9 @@ namespace Artemis.Storage.Migrations
BlueScale = table.Column<float>(type: "REAL", nullable: false),
IsEnabled = table.Column<bool>(type: "INTEGER", nullable: false),
PhysicalLayout = table.Column<int>(type: "INTEGER", nullable: false),
LogicalLayout = table.Column<string>(type: "TEXT", nullable: true),
LayoutType = table.Column<string>(type: "TEXT", nullable: true),
LayoutParameter = table.Column<string>(type: "TEXT", nullable: true),
LogicalLayout = table.Column<string>(type: "TEXT", maxLength: 32, nullable: true),
LayoutType = table.Column<string>(type: "TEXT", maxLength: 64, nullable: true),
LayoutParameter = table.Column<string>(type: "TEXT", maxLength: 512, nullable: true),
Categories = table.Column<string>(type: "TEXT", nullable: false),
InputIdentifiers = table.Column<string>(type: "TEXT", nullable: false),
InputMappings = table.Column<string>(type: "TEXT", nullable: false)
@ -51,7 +51,7 @@ namespace Artemis.Storage.Migrations
ReleaseId = table.Column<long>(type: "INTEGER", nullable: false),
ReleaseVersion = table.Column<string>(type: "TEXT", nullable: false),
InstalledAt = table.Column<DateTimeOffset>(type: "TEXT", nullable: false),
Metadata = table.Column<string>(type: "TEXT", nullable: false)
Metadata = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
@ -63,6 +63,7 @@ namespace Artemis.Storage.Migrations
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
PluginGuid = table.Column<Guid>(type: "TEXT", nullable: false),
IsEnabled = table.Column<bool>(type: "INTEGER", nullable: false)
},
constraints: table =>
@ -76,7 +77,7 @@ namespace Artemis.Storage.Migrations
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
PluginGuid = table.Column<Guid>(type: "TEXT", nullable: false),
Name = table.Column<string>(type: "TEXT", nullable: false),
Name = table.Column<string>(type: "TEXT", maxLength: 128, nullable: false),
Value = table.Column<string>(type: "TEXT", nullable: false)
},
constraints: table =>
@ -89,7 +90,7 @@ namespace Artemis.Storage.Migrations
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
Name = table.Column<string>(type: "TEXT", nullable: false),
Name = table.Column<string>(type: "TEXT", maxLength: 64, nullable: false),
IsCollapsed = table.Column<bool>(type: "INTEGER", nullable: false),
IsSuspended = table.Column<bool>(type: "INTEGER", nullable: false),
Order = table.Column<int>(type: "INTEGER", nullable: false)
@ -104,7 +105,7 @@ namespace Artemis.Storage.Migrations
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
Version = table.Column<string>(type: "TEXT", nullable: false),
Version = table.Column<string>(type: "TEXT", maxLength: 64, nullable: false),
InstalledAt = table.Column<DateTimeOffset>(type: "TEXT", nullable: true)
},
constraints: table =>
@ -113,7 +114,7 @@ namespace Artemis.Storage.Migrations
});
migrationBuilder.CreateTable(
name: "PluginFeatureEntity",
name: "PluginFeatures",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
@ -123,16 +124,16 @@ namespace Artemis.Storage.Migrations
},
constraints: table =>
{
table.PrimaryKey("PK_PluginFeatureEntity", x => x.Id);
table.PrimaryKey("PK_PluginFeatures", x => x.Id);
table.ForeignKey(
name: "FK_PluginFeatureEntity_Plugins_PluginEntityId",
name: "FK_PluginFeatures_Plugins_PluginEntityId",
column: x => x.PluginEntityId,
principalTable: "Plugins",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "ProfileContainerEntity",
name: "ProfileContainers",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
@ -143,9 +144,9 @@ namespace Artemis.Storage.Migrations
},
constraints: table =>
{
table.PrimaryKey("PK_ProfileContainerEntity", x => x.Id);
table.PrimaryKey("PK_ProfileContainers", x => x.Id);
table.ForeignKey(
name: "FK_ProfileContainerEntity_ProfileCategories_ProfileCategoryId",
name: "FK_ProfileContainers_ProfileCategories_ProfileCategoryId",
column: x => x.ProfileCategoryId,
principalTable: "ProfileCategories",
principalColumn: "Id",
@ -153,14 +154,54 @@ namespace Artemis.Storage.Migrations
});
migrationBuilder.CreateIndex(
name: "IX_PluginFeatureEntity_PluginEntityId",
table: "PluginFeatureEntity",
name: "IX_Entries_EntryId",
table: "Entries",
column: "EntryId",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_PluginFeatures_PluginEntityId",
table: "PluginFeatures",
column: "PluginEntityId");
migrationBuilder.CreateIndex(
name: "IX_ProfileContainerEntity_ProfileCategoryId",
table: "ProfileContainerEntity",
name: "IX_Plugins_PluginGuid",
table: "Plugins",
column: "PluginGuid",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_PluginSettings_Name_PluginGuid",
table: "PluginSettings",
columns: new[] { "Name", "PluginGuid" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_PluginSettings_PluginGuid",
table: "PluginSettings",
column: "PluginGuid");
migrationBuilder.CreateIndex(
name: "IX_ProfileCategories_Name",
table: "ProfileCategories",
column: "Name",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ProfileContainers_ProfileCategoryId",
table: "ProfileContainers",
column: "ProfileCategoryId");
migrationBuilder.CreateIndex(
name: "IX_Releases_InstalledAt",
table: "Releases",
column: "InstalledAt");
migrationBuilder.CreateIndex(
name: "IX_Releases_Version",
table: "Releases",
column: "Version",
unique: true);
}
/// <inheritdoc />
@ -173,13 +214,13 @@ namespace Artemis.Storage.Migrations
name: "Entries");
migrationBuilder.DropTable(
name: "PluginFeatureEntity");
name: "PluginFeatures");
migrationBuilder.DropTable(
name: "PluginSettings");
migrationBuilder.DropTable(
name: "ProfileContainerEntity");
name: "ProfileContainers");
migrationBuilder.DropTable(
name: "Releases");

View File

@ -28,10 +28,16 @@ namespace Artemis.Storage.Migrations
b.Property<string>("Version")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("InstalledAt");
b.HasIndex("Version")
.IsUnique();
b.ToTable("Releases");
});
@ -44,8 +50,14 @@ namespace Artemis.Storage.Migrations
b.Property<bool>("IsEnabled")
.HasColumnType("INTEGER");
b.Property<Guid>("PluginGuid")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("PluginGuid")
.IsUnique();
b.ToTable("Plugins");
});
@ -69,7 +81,7 @@ namespace Artemis.Storage.Migrations
b.HasIndex("PluginEntityId");
b.ToTable("PluginFeatureEntity");
b.ToTable("PluginFeatures");
});
modelBuilder.Entity("Artemis.Storage.Entities.Plugins.PluginSettingEntity", b =>
@ -80,6 +92,7 @@ namespace Artemis.Storage.Migrations
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("TEXT");
b.Property<Guid>("PluginGuid")
@ -91,6 +104,11 @@ namespace Artemis.Storage.Migrations
b.HasKey("Id");
b.HasIndex("PluginGuid");
b.HasIndex("Name", "PluginGuid")
.IsUnique();
b.ToTable("PluginSettings");
});
@ -108,6 +126,7 @@ namespace Artemis.Storage.Migrations
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("TEXT");
b.Property<int>("Order")
@ -115,6 +134,9 @@ namespace Artemis.Storage.Migrations
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("ProfileCategories");
});
@ -143,12 +165,13 @@ namespace Artemis.Storage.Migrations
b.HasIndex("ProfileCategoryId");
b.ToTable("ProfileContainerEntity");
b.ToTable("ProfileContainers");
});
modelBuilder.Entity("Artemis.Storage.Entities.Surface.DeviceEntity", b =>
{
b.Property<string>("Id")
.HasMaxLength(512)
.HasColumnType("TEXT");
b.Property<float>("BlueScale")
@ -160,6 +183,7 @@ namespace Artemis.Storage.Migrations
b.Property<string>("DeviceProvider")
.IsRequired()
.HasMaxLength(512)
.HasColumnType("TEXT");
b.Property<float>("GreenScale")
@ -169,12 +193,15 @@ namespace Artemis.Storage.Migrations
.HasColumnType("INTEGER");
b.Property<string>("LayoutParameter")
.HasMaxLength(512)
.HasColumnType("TEXT");
b.Property<string>("LayoutType")
.HasMaxLength(64)
.HasColumnType("TEXT");
b.Property<string>("LogicalLayout")
.HasMaxLength(32)
.HasColumnType("TEXT");
b.Property<int>("PhysicalLayout")
@ -223,7 +250,6 @@ namespace Artemis.Storage.Migrations
.HasColumnType("TEXT");
b.Property<string>("Metadata")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Name")
@ -239,6 +265,9 @@ namespace Artemis.Storage.Migrations
b.HasKey("Id");
b.HasIndex("EntryId")
.IsUnique();
b.ToTable("Entries");
});

View File

@ -1,43 +1,50 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using Artemis.Storage.Entities.Surface;
using Artemis.Storage.Repositories.Interfaces;
namespace Artemis.Storage.Repositories;
internal class DeviceRepository : IDeviceRepository
internal class DeviceRepository(Func<ArtemisDbContext> getContext) : IDeviceRepository
{
private readonly ArtemisDbContext _dbContext;
public DeviceRepository(ArtemisDbContext dbContext)
{
_dbContext = dbContext;
}
public void Add(DeviceEntity deviceEntity)
{
_dbContext.Devices.Add(deviceEntity);
SaveChanges();
using ArtemisDbContext dbContext = getContext();
dbContext.Devices.Add(deviceEntity);
dbContext.SaveChanges();
}
public void Remove(DeviceEntity deviceEntity)
{
_dbContext.Devices.Remove(deviceEntity);
SaveChanges();
using ArtemisDbContext dbContext = getContext();
dbContext.Devices.Remove(deviceEntity);
dbContext.SaveChanges();
}
public DeviceEntity? Get(string id)
{
return _dbContext.Devices.FirstOrDefault(d => d.Id == id);
using ArtemisDbContext dbContext = getContext();
return dbContext.Devices.FirstOrDefault(d => d.Id == id);
}
public IEnumerable<DeviceEntity> GetAll()
{
return _dbContext.Devices;
using ArtemisDbContext dbContext = getContext();
return dbContext.Devices;
}
public void SaveChanges()
public void Save(DeviceEntity deviceEntity)
{
_dbContext.SaveChanges();
using ArtemisDbContext dbContext = getContext();
dbContext.Update(deviceEntity);
dbContext.SaveChanges();
}
public void SaveRange(IEnumerable<DeviceEntity> deviceEntities)
{
using ArtemisDbContext dbContext = getContext();
dbContext.UpdateRange(deviceEntities);
dbContext.SaveChanges();
}
}

View File

@ -6,44 +6,44 @@ using Artemis.Storage.Repositories.Interfaces;
namespace Artemis.Storage.Repositories;
internal class EntryRepository : IEntryRepository
internal class EntryRepository(Func<ArtemisDbContext> getContext) : IEntryRepository
{
private readonly ArtemisDbContext _dbContext;
public EntryRepository(ArtemisDbContext dbContext)
{
_dbContext = dbContext;
}
public void Add(EntryEntity entryEntity)
{
_dbContext.Entries.Add(entryEntity);
SaveChanges();
using ArtemisDbContext dbContext = getContext();
dbContext.Entries.Add(entryEntity);
dbContext.SaveChanges();
}
public void Remove(EntryEntity entryEntity)
{
_dbContext.Entries.Remove(entryEntity);
SaveChanges();
using ArtemisDbContext dbContext = getContext();
dbContext.Entries.Remove(entryEntity);
dbContext.SaveChanges();
}
public EntryEntity? Get(Guid id)
{
return _dbContext.Entries.FirstOrDefault(s => s.Id == id);
using ArtemisDbContext dbContext = getContext();
return dbContext.Entries.FirstOrDefault(s => s.Id == id);
}
public EntryEntity? GetByEntryId(long entryId)
{
return _dbContext.Entries.FirstOrDefault(s => s.EntryId == entryId);
using ArtemisDbContext dbContext = getContext();
return dbContext.Entries.FirstOrDefault(s => s.EntryId == entryId);
}
public IEnumerable<EntryEntity> GetAll()
{
return _dbContext.Entries;
using ArtemisDbContext dbContext = getContext();
return dbContext.Entries;
}
public void SaveChanges()
public void Save(EntryEntity entryEntity)
{
_dbContext.SaveChanges();
using ArtemisDbContext dbContext = getContext();
dbContext.Update(entryEntity);
dbContext.SaveChanges();
}
}

View File

@ -9,5 +9,6 @@ public interface IDeviceRepository : IRepository
void Remove(DeviceEntity deviceEntity);
DeviceEntity? Get(string id);
IEnumerable<DeviceEntity> GetAll();
void SaveChanges();
void Save(DeviceEntity deviceEntity);
void SaveRange(IEnumerable<DeviceEntity> deviceEntities);
}

View File

@ -11,5 +11,5 @@ public interface IEntryRepository : IRepository
EntryEntity? Get(Guid id);
EntryEntity? GetByEntryId(long entryId);
IEnumerable<EntryEntity> GetAll();
void SaveChanges();
void Save(EntryEntity entryEntity);
}

View File

@ -5,10 +5,9 @@ namespace Artemis.Storage.Repositories.Interfaces;
public interface IPluginRepository : IRepository
{
void AddPlugin(PluginEntity pluginEntity);
PluginEntity? GetPluginByGuid(Guid pluginGuid);
void AddSetting(PluginSettingEntity pluginSettingEntity);
PluginEntity? GetPluginByPluginGuid(Guid pluginGuid);
void SaveSetting(PluginSettingEntity pluginSettingEntity);
void SavePlugin(PluginEntity pluginEntity);
PluginSettingEntity? GetSettingByNameAndGuid(string name, Guid pluginGuid);
void RemoveSettings(Guid pluginGuid);
void SaveChanges();
}

View File

@ -12,5 +12,6 @@ public interface IProfileCategoryRepository : IRepository
List<ProfileCategoryEntity> GetAll();
ProfileCategoryEntity? Get(Guid id);
bool IsUnique(string name, Guid? id);
void SaveChanges();
void Save(ProfileCategoryEntity profileCategoryEntity);
void SaveRange(List<ProfileCategoryEntity> profileCategoryEntities);
}

View File

@ -0,0 +1,15 @@
using System.Collections.Generic;
using System.Text.Json.Nodes;
using Artemis.Storage.Entities.Profile;
namespace Artemis.Storage.Repositories.Interfaces;
public interface IProfileRepository : IRepository
{
void Add(ProfileContainerEntity profileContainerEntity);
void Remove(ProfileContainerEntity profileContainerEntity);
void Save(ProfileContainerEntity profileContainerEntity);
void SaveRange(List<ProfileContainerEntity> profileContainerEntities);
void MigrateProfiles();
void MigrateProfile(JsonObject? configurationJson, JsonObject? profileJson);
}

View File

@ -0,0 +1,9 @@
using Artemis.Storage.Entities.General;
namespace Artemis.Storage.Repositories.Interfaces;
public interface IReleaseRepository : IRepository
{
bool SaveVersionInstallDate(string version);
ReleaseEntity? GetPreviousInstalledVersion();
}

View File

@ -6,44 +6,39 @@ using Microsoft.EntityFrameworkCore;
namespace Artemis.Storage.Repositories;
internal class PluginRepository : IPluginRepository
internal class PluginRepository(Func<ArtemisDbContext> getContext) : IPluginRepository
{
private readonly ArtemisDbContext _dbContext;
public PluginRepository(ArtemisDbContext dbContext)
public PluginEntity? GetPluginByPluginGuid(Guid pluginGuid)
{
_dbContext = dbContext;
}
public void AddPlugin(PluginEntity pluginEntity)
{
_dbContext.Plugins.Add(pluginEntity);
SaveChanges();
}
public PluginEntity? GetPluginByGuid(Guid pluginGuid)
{
return _dbContext.Plugins.Include(p => p.Features).FirstOrDefault(p => p.Id == pluginGuid);
}
public void AddSetting(PluginSettingEntity pluginSettingEntity)
{
_dbContext.PluginSettings.Add(pluginSettingEntity);
SaveChanges();
using ArtemisDbContext dbContext = getContext();
return dbContext.Plugins.Include(p => p.Features).FirstOrDefault(p => p.PluginGuid == pluginGuid);
}
public PluginSettingEntity? GetSettingByNameAndGuid(string name, Guid pluginGuid)
{
return _dbContext.PluginSettings.FirstOrDefault(p => p.Name == name && p.PluginGuid == pluginGuid);
using ArtemisDbContext dbContext = getContext();
return dbContext.PluginSettings.FirstOrDefault(p => p.Name == name && p.PluginGuid == pluginGuid);
}
public void RemoveSettings(Guid pluginGuid)
{
_dbContext.PluginSettings.RemoveRange(_dbContext.PluginSettings.Where(s => s.PluginGuid == pluginGuid));
using ArtemisDbContext dbContext = getContext();
dbContext.PluginSettings.RemoveRange(dbContext.PluginSettings.Where(s => s.PluginGuid == pluginGuid));
dbContext.SaveChanges();
}
public void SaveChanges()
public void SaveSetting(PluginSettingEntity pluginSettingEntity)
{
_dbContext.SaveChanges();
using ArtemisDbContext dbContext = getContext();
dbContext.PluginSettings.Update(pluginSettingEntity);
dbContext.SaveChanges();
}
public void SavePlugin(PluginEntity pluginEntity)
{
using ArtemisDbContext dbContext = getContext();
dbContext.Update(pluginEntity);
dbContext.SaveChanges();
}
}

View File

@ -7,47 +7,63 @@ using Microsoft.EntityFrameworkCore;
namespace Artemis.Storage.Repositories;
internal class ProfileCategoryRepository : IProfileCategoryRepository
internal class ProfileCategoryRepository(Func<ArtemisDbContext> getContext, IProfileRepository profileRepository) : IProfileCategoryRepository
{
private readonly ArtemisDbContext _dbContext;
public ProfileCategoryRepository(ArtemisDbContext dbContext)
{
_dbContext = dbContext;
}
private bool _migratedProfiles;
public void Add(ProfileCategoryEntity profileCategoryEntity)
{
_dbContext.ProfileCategories.Add(profileCategoryEntity);
SaveChanges();
using ArtemisDbContext dbContext = getContext();
dbContext.ProfileCategories.Add(profileCategoryEntity);
dbContext.SaveChanges();
}
public void Remove(ProfileCategoryEntity profileCategoryEntity)
{
_dbContext.ProfileCategories.Remove(profileCategoryEntity);
SaveChanges();
using ArtemisDbContext dbContext = getContext();
dbContext.ProfileCategories.Remove(profileCategoryEntity);
dbContext.SaveChanges();
}
public List<ProfileCategoryEntity> GetAll()
{
return _dbContext.ProfileCategories.Include(c => c.ProfileConfigurations).ToList();
if (!_migratedProfiles)
{
profileRepository.MigrateProfiles();
_migratedProfiles = true;
}
using ArtemisDbContext dbContext = getContext();
return dbContext.ProfileCategories.Include(c => c.ProfileConfigurations).ToList();
}
public ProfileCategoryEntity? Get(Guid id)
{
return _dbContext.ProfileCategories.Include(c => c.ProfileConfigurations).FirstOrDefault(c => c.Id == id);
using ArtemisDbContext dbContext = getContext();
return dbContext.ProfileCategories.Include(c => c.ProfileConfigurations).FirstOrDefault(c => c.Id == id);
}
public void Save(ProfileCategoryEntity profileCategoryEntity)
{
using ArtemisDbContext dbContext = getContext();
dbContext.Update(profileCategoryEntity);
dbContext.SaveChanges();
}
public void SaveRange(List<ProfileCategoryEntity> profileCategoryEntities)
{
using ArtemisDbContext dbContext = getContext();
dbContext.UpdateRange(profileCategoryEntities);
dbContext.SaveChanges();
}
public bool IsUnique(string name, Guid? id)
{
using ArtemisDbContext dbContext = getContext();
name = name.Trim();
if (id == null)
return _dbContext.ProfileCategories.Any(p => p.Name == name);
return _dbContext.ProfileCategories.Any(p => p.Name == name && p.Id != id.Value);
}
public void SaveChanges()
{
_dbContext.SaveChanges();
return id == null
? dbContext.ProfileCategories.Any(p => p.Name == name)
: dbContext.ProfileCategories.Any(p => p.Name == name && p.Id != id.Value);
}
}

View File

@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Nodes;
using Artemis.Storage.Entities;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Exceptions;
using Artemis.Storage.Migrations;
using Artemis.Storage.Repositories.Interfaces;
using Microsoft.EntityFrameworkCore;
namespace Artemis.Storage.Repositories;
public class ProfileRepository(Func<ArtemisDbContext> getContext, List<IProfileMigration> profileMigrators) : IProfileRepository
{
public void Add(ProfileContainerEntity profileContainerEntity)
{
using ArtemisDbContext dbContext = getContext();
dbContext.ProfileContainers.Add(profileContainerEntity);
dbContext.SaveChanges();
}
public void Remove(ProfileContainerEntity profileContainerEntity)
{
using ArtemisDbContext dbContext = getContext();
dbContext.ProfileContainers.Remove(profileContainerEntity);
dbContext.SaveChanges();
}
public void Save(ProfileContainerEntity profileContainerEntity)
{
using ArtemisDbContext dbContext = getContext();
dbContext.Update(profileContainerEntity);
dbContext.SaveChanges();
}
public void SaveRange(List<ProfileContainerEntity> profileContainerEntities)
{
using ArtemisDbContext dbContext = getContext();
dbContext.UpdateRange(profileContainerEntities);
dbContext.SaveChanges();
}
public void MigrateProfiles()
{
using ArtemisDbContext dbContext = getContext();
int max = profileMigrators.Max(m => m.Version);
// Query the ProfileContainerEntity table directly, grabbing the ID, profile, and configuration
List<RawProfileContainer> containers = dbContext.Database
.SqlQueryRaw<RawProfileContainer>("SELECT Id, Profile, ProfileConfiguration FROM ProfileContainers WHERE json_extract(ProfileConfiguration, '$.Version') < {0}", max)
.ToList();
foreach (RawProfileContainer rawProfileContainer in containers)
{
JsonObject? profileConfiguration = JsonNode.Parse(rawProfileContainer.ProfileConfiguration)?.AsObject();
JsonObject? profile = JsonNode.Parse(rawProfileContainer.Profile)?.AsObject();
if (profileConfiguration == null || profile == null)
throw new ArtemisStorageException("Failed to parse profile or profile configuration");
MigrateProfile(profileConfiguration, profile);
rawProfileContainer.Profile = profile.ToString();
rawProfileContainer.ProfileConfiguration = profileConfiguration.ToString();
// Write the updated containers back to the database
dbContext.Database.ExecuteSqlRaw(
"UPDATE ProfileContainers SET Profile = {0}, ProfileConfiguration = {1} WHERE Id = {2}",
rawProfileContainer.Profile,
rawProfileContainer.ProfileConfiguration,
rawProfileContainer.Id);
}
}
public void MigrateProfile(JsonObject? configurationJson, JsonObject? profileJson)
{
if (configurationJson == null || profileJson == null)
return;
configurationJson["Version"] ??= 0;
foreach (IProfileMigration profileMigrator in profileMigrators.OrderBy(m => m.Version))
{
if (profileMigrator.Version <= configurationJson["Version"]!.GetValue<int>())
continue;
profileMigrator.Migrate(configurationJson, profileJson);
configurationJson["Version"] = profileMigrator.Version;
}
}
}

View File

@ -5,34 +5,24 @@ using Artemis.Storage.Repositories.Interfaces;
namespace Artemis.Storage.Repositories;
public class ReleaseRepository : IReleaseRepository
public class ReleaseRepository(Func<ArtemisDbContext> getContext) : IReleaseRepository
{
private readonly ArtemisDbContext _dbContext;
public ReleaseRepository(ArtemisDbContext dbContext)
{
_dbContext = dbContext;
}
public bool SaveVersionInstallDate(string version)
{
ReleaseEntity? release = _dbContext.Releases.FirstOrDefault(r => r.Version == version);
using ArtemisDbContext dbContext = getContext();
ReleaseEntity? release = dbContext.Releases.FirstOrDefault(r => r.Version == version);
if (release != null)
return false;
_dbContext.Releases.Add(new ReleaseEntity {Version = version, InstalledAt = DateTimeOffset.UtcNow});
_dbContext.SaveChanges();
dbContext.Releases.Add(new ReleaseEntity {Version = version, InstalledAt = DateTimeOffset.UtcNow});
dbContext.SaveChanges();
return true;
}
public ReleaseEntity? GetPreviousInstalledVersion()
{
return _dbContext.Releases.OrderByDescending(r => r.InstalledAt).Skip(1).FirstOrDefault();
using ArtemisDbContext dbContext = getContext();
return dbContext.Releases.OrderByDescending(r => r.InstalledAt).Skip(1).FirstOrDefault();
}
}
public interface IReleaseRepository : IRepository
{
bool SaveVersionInstallDate(string version);
ReleaseEntity? GetPreviousInstalledVersion();
}

View File

@ -1,5 +1,6 @@
using System;
using Artemis.Core.Services;
using Artemis.Storage.Legacy;
using Artemis.UI.Linux.DryIoc;
using Artemis.UI.Linux.Providers.Input;
using Avalonia;
@ -20,7 +21,10 @@ public class App : Application
public override void Initialize()
{
_container = ArtemisBootstrapper.Bootstrap(this, c => c.RegisterProviders());
Program.CreateLogger(_container);
LegacyMigrationService.MigrateToSqlite(_container);
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
AvaloniaXamlLoader.Load(this);
}

View File

@ -17,6 +17,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
<ProjectReference Include="..\Artemis.Storage.Legacy\Artemis.Storage.Legacy.csproj" />
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj" />
</ItemGroup>
<ItemGroup>

View File

@ -1,3 +1,4 @@
using Artemis.Storage.Legacy;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
@ -14,7 +15,10 @@ public class App : Application
public override void Initialize()
{
_container = ArtemisBootstrapper.Bootstrap(this);
Program.CreateLogger(_container);
LegacyMigrationService.MigrateToSqlite(_container);
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
AvaloniaXamlLoader.Load(this);
}

View File

@ -16,6 +16,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
<ProjectReference Include="..\Artemis.Storage.Legacy\Artemis.Storage.Legacy.csproj" />
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj" />
</ItemGroup>
</Project>

View File

@ -21,6 +21,7 @@
<PackageReference Include="Material.Icons.Avalonia" />
<PackageReference Include="ReactiveUI" />
<PackageReference Include="ReactiveUI.Validation" />
<PackageReference Include="JetBrains.Annotations" PrivateAssets="All"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />

View File

@ -7,6 +7,7 @@ using System.Net.Http;
using System.Threading;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.Storage.Legacy;
using Artemis.UI.Windows.DryIoc;
using Artemis.UI.Windows.Providers.Input;
using Avalonia;
@ -34,7 +35,10 @@ public class App : Application
}
_container = ArtemisBootstrapper.Bootstrap(this, c => c.RegisterProviders());
Program.CreateLogger(_container);
LegacyMigrationService.MigrateToSqlite(_container);
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
AvaloniaXamlLoader.Load(this);
}

View File

@ -30,6 +30,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
<ProjectReference Include="..\Artemis.Storage.Legacy\Artemis.Storage.Legacy.csproj" />
<ProjectReference Include="..\Artemis.UI\Artemis.UI.csproj" />
</ItemGroup>
</Project>

Some files were not shown because too many files have changed in this diff Show More