diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 88f5e777e3..974c023a20 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -106,7 +106,10 @@ namespace osu.Game.Beatmaps
foreach (BeatmapInfo b in beatmapSet.Beatmaps)
fetchAndPopulateOnlineValues(b, beatmapSet.Beatmaps);
+ }
+ protected override void PreImport(BeatmapSetInfo beatmapSet)
+ {
// check if a set already exists with the same online id, delete if it does.
if (beatmapSet.OnlineBeatmapSetID != null)
{
@@ -254,6 +257,15 @@ namespace osu.Game.Beatmaps
/// The first result for the provided query, or null if no results were found.
public BeatmapSetInfo QueryBeatmapSet(Expression> query) => beatmaps.ConsumableItems.AsNoTracking().FirstOrDefault(query);
+ protected override bool CanUndelete(BeatmapSetInfo existing, BeatmapSetInfo import)
+ {
+ if (!base.CanUndelete(existing, import))
+ return false;
+
+ // force re-import if we are not in a sane state.
+ return existing.OnlineBeatmapSetID == import.OnlineBeatmapSetID;
+ }
+
///
/// Returns a list of all usable s.
///
diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs
index 9ec184abd7..3805921ac2 100644
--- a/osu.Game/Database/ArchiveModelManager.cs
+++ b/osu.Game/Database/ArchiveModelManager.cs
@@ -300,21 +300,31 @@ namespace osu.Game.Database
{
if (!write.IsTransactionLeader) throw new InvalidOperationException($"Ensure there is no parent transaction so errors can correctly be handled by {this}");
- var existing = CheckForExisting(item);
-
- if (existing != null)
- {
- Undelete(existing);
- Logger.Log($"Found existing {typeof(TModel)} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database);
- handleEvent(() => ItemAdded?.Invoke(existing, true));
- return existing;
- }
-
if (archive != null)
item.Files = createFileInfos(archive, Files);
Populate(item, archive);
+ var existing = CheckForExisting(item);
+
+ if (existing != null)
+ {
+ if (CanUndelete(existing, item))
+ {
+ Undelete(existing);
+ Logger.Log($"Found existing {typeof(TModel)} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database);
+ handleEvent(() => ItemAdded?.Invoke(existing, true));
+ return existing;
+ }
+ else
+ {
+ Delete(existing);
+ ModelStore.PurgeDeletable(s => s.ID == existing.ID);
+ }
+ }
+
+ PreImport(item);
+
// import to store
ModelStore.Add(item);
}
@@ -542,12 +552,29 @@ namespace osu.Game.Database
{
}
+ ///
+ /// Perform any final actions before the import to database executes.
+ ///
+ /// The model prepared for import.
+ protected virtual void PreImport(TModel model)
+ {
+ }
+
///
/// Check whether an existing model already exists for a new import item.
///
- /// The new model proposed for import. Note that has not yet been run on this model.
+ /// The new model proposed for import.
/// An existing model which matches the criteria to skip importing, else null.
- protected virtual TModel CheckForExisting(TModel model) => model.Hash == null ? null : ModelStore.ConsumableItems.FirstOrDefault(b => b.Hash == model.Hash);
+ protected TModel CheckForExisting(TModel model) => model.Hash == null ? null : ModelStore.ConsumableItems.FirstOrDefault(b => b.Hash == model.Hash);
+
+ ///
+ /// After an existing is found during an import process, the default behaviour is to restore the existing
+ /// item and skip the import. This method allows changing that behaviour.
+ ///
+ /// The existing model.
+ /// The newly imported model.
+ /// Whether the existing model should be restored and used. Returning false will delete the existing a force a re-import.
+ protected virtual bool CanUndelete(TModel existing, TModel import) => true;
private DbSet queryModel() => ContextFactory.Get().Set();