mirror of
https://github.com/ppy/osu.git
synced 2025-01-30 06:12:58 +08:00
Add back transaction support for beatmap importing
This commit is contained in:
parent
fe44a28d48
commit
cd41862e3b
@ -57,6 +57,18 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
private readonly Storage storage;
|
private readonly Storage storage;
|
||||||
|
|
||||||
|
private BeatmapStore createBeatmapStore(Func<OsuDbContext> context)
|
||||||
|
{
|
||||||
|
var store = new BeatmapStore(context);
|
||||||
|
store.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s);
|
||||||
|
store.BeatmapSetRemoved += s => BeatmapSetRemoved?.Invoke(s);
|
||||||
|
store.BeatmapHidden += b => BeatmapHidden?.Invoke(b);
|
||||||
|
store.BeatmapRestored += b => BeatmapRestored?.Invoke(b);
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly Func<OsuDbContext> createContext;
|
||||||
|
|
||||||
private readonly FileStore files;
|
private readonly FileStore files;
|
||||||
|
|
||||||
private readonly RulesetStore rulesets;
|
private readonly RulesetStore rulesets;
|
||||||
@ -80,16 +92,13 @@ namespace osu.Game.Beatmaps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<Storage> GetStableStorage { private get; set; }
|
public Func<Storage> GetStableStorage { private get; set; }
|
||||||
|
|
||||||
public BeatmapManager(Storage storage, FileStore files, OsuDbContext context, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null)
|
public BeatmapManager(Storage storage, Func<OsuDbContext> context, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null)
|
||||||
{
|
{
|
||||||
beatmaps = new BeatmapStore(context);
|
createContext = context;
|
||||||
beatmaps.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s);
|
beatmaps = createBeatmapStore(context);
|
||||||
beatmaps.BeatmapSetRemoved += s => BeatmapSetRemoved?.Invoke(s);
|
files = new FileStore(context, storage);
|
||||||
beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b);
|
|
||||||
beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b);
|
|
||||||
|
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
this.files = files;
|
|
||||||
this.rulesets = rulesets;
|
this.rulesets = rulesets;
|
||||||
this.api = api;
|
this.api = api;
|
||||||
|
|
||||||
@ -160,13 +169,24 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <param name="archiveReader">The beatmap to be imported.</param>
|
/// <param name="archiveReader">The beatmap to be imported.</param>
|
||||||
public BeatmapSetInfo Import(ArchiveReader archiveReader)
|
public BeatmapSetInfo Import(ArchiveReader archiveReader)
|
||||||
{
|
{
|
||||||
BeatmapSetInfo set;
|
|
||||||
|
|
||||||
// let's only allow one concurrent import at a time for now.
|
// let's only allow one concurrent import at a time for now.
|
||||||
lock (importLock)
|
lock (importLock)
|
||||||
Import(set = importToStorage(archiveReader));
|
{
|
||||||
|
var context = createContext();
|
||||||
|
|
||||||
return set;
|
using (var transaction = context.Database.BeginTransaction())
|
||||||
|
{
|
||||||
|
// create local stores so we can isolate and thread safely, and share a context/transaction.
|
||||||
|
var filesForImport = new FileStore(() => context, storage);
|
||||||
|
var beatmapsForImport = createBeatmapStore(() => context);
|
||||||
|
|
||||||
|
BeatmapSetInfo set = importToStorage(filesForImport, archiveReader);
|
||||||
|
beatmapsForImport.Add(set);
|
||||||
|
context.SaveChanges();
|
||||||
|
transaction.Commit();
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -178,8 +198,7 @@ namespace osu.Game.Beatmaps
|
|||||||
// If we have an ID then we already exist in the database.
|
// If we have an ID then we already exist in the database.
|
||||||
if (beatmapSetInfo.ID != 0) return;
|
if (beatmapSetInfo.ID != 0) return;
|
||||||
|
|
||||||
lock (beatmaps)
|
createBeatmapStore(createContext).Add(beatmapSetInfo);
|
||||||
beatmaps.Add(beatmapSetInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -322,15 +341,6 @@ namespace osu.Game.Beatmaps
|
|||||||
return working;
|
return working;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reset the manager to an empty state.
|
|
||||||
/// </summary>
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
lock (beatmaps)
|
|
||||||
beatmaps.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Perform a lookup query on available <see cref="BeatmapSetInfo"/>s.
|
/// Perform a lookup query on available <see cref="BeatmapSetInfo"/>s.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -400,7 +410,7 @@ namespace osu.Game.Beatmaps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="reader">The beatmap archive to be read.</param>
|
/// <param name="reader">The beatmap archive to be read.</param>
|
||||||
/// <returns>The imported beatmap, or an existing instance if it is already present.</returns>
|
/// <returns>The imported beatmap, or an existing instance if it is already present.</returns>
|
||||||
private BeatmapSetInfo importToStorage(ArchiveReader reader)
|
private BeatmapSetInfo importToStorage(FileStore files, ArchiveReader reader)
|
||||||
{
|
{
|
||||||
// let's make sure there are actually .osu files to import.
|
// let's make sure there are actually .osu files to import.
|
||||||
string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu"));
|
string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu"));
|
||||||
|
@ -20,8 +20,8 @@ namespace osu.Game.Beatmaps
|
|||||||
public event Action<BeatmapInfo> BeatmapHidden;
|
public event Action<BeatmapInfo> BeatmapHidden;
|
||||||
public event Action<BeatmapInfo> BeatmapRestored;
|
public event Action<BeatmapInfo> BeatmapRestored;
|
||||||
|
|
||||||
public BeatmapStore(OsuDbContext context)
|
public BeatmapStore(Func<OsuDbContext> factory)
|
||||||
: base(context)
|
: base(factory)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,12 +10,15 @@ namespace osu.Game.Database
|
|||||||
public abstract class DatabaseBackedStore
|
public abstract class DatabaseBackedStore
|
||||||
{
|
{
|
||||||
protected readonly Storage Storage;
|
protected readonly Storage Storage;
|
||||||
protected readonly OsuDbContext Context;
|
|
||||||
|
|
||||||
protected DatabaseBackedStore(OsuDbContext context, Storage storage = null)
|
private readonly Func<OsuDbContext> contextSource;
|
||||||
|
|
||||||
|
protected OsuDbContext Context => contextSource();
|
||||||
|
|
||||||
|
protected DatabaseBackedStore(Func<OsuDbContext> contextSource, Storage storage = null)
|
||||||
{
|
{
|
||||||
Storage = storage;
|
Storage = storage;
|
||||||
Context = context;
|
this.contextSource = contextSource;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
19
osu.Game/Database/DatabaseContextFactory.cs
Normal file
19
osu.Game/Database/DatabaseContextFactory.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||||
|
|
||||||
|
using osu.Framework.Platform;
|
||||||
|
|
||||||
|
namespace osu.Game.Database
|
||||||
|
{
|
||||||
|
public class DatabaseContextFactory
|
||||||
|
{
|
||||||
|
private readonly GameHost host;
|
||||||
|
|
||||||
|
public DatabaseContextFactory(GameHost host)
|
||||||
|
{
|
||||||
|
this.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OsuDbContext GetContext() => new OsuDbContext(host.Storage.GetDatabaseConnectionString(@"client"));
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,7 @@ namespace osu.Game.IO
|
|||||||
|
|
||||||
public readonly ResourceStore<byte[]> Store;
|
public readonly ResourceStore<byte[]> Store;
|
||||||
|
|
||||||
public FileStore(OsuDbContext context, Storage storage) : base(context, storage)
|
public FileStore(Func<OsuDbContext> contextSource, Storage storage) : base(contextSource, storage)
|
||||||
{
|
{
|
||||||
Store = new NamespacedResourceStore<byte[]>(new StorageBackedResourceStore(storage), prefix);
|
Store = new NamespacedResourceStore<byte[]>(new StorageBackedResourceStore(storage), prefix);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@ -14,8 +15,8 @@ namespace osu.Game.Input
|
|||||||
{
|
{
|
||||||
public class KeyBindingStore : DatabaseBackedStore
|
public class KeyBindingStore : DatabaseBackedStore
|
||||||
{
|
{
|
||||||
public KeyBindingStore(OsuDbContext context, RulesetStore rulesets, Storage storage = null)
|
public KeyBindingStore(Func<OsuDbContext> contextSource, RulesetStore rulesets, Storage storage = null)
|
||||||
: base(context, storage)
|
: base(contextSource, storage)
|
||||||
{
|
{
|
||||||
foreach (var info in rulesets.AvailableRulesets)
|
foreach (var info in rulesets.AvailableRulesets)
|
||||||
{
|
{
|
||||||
|
@ -81,16 +81,18 @@ namespace osu.Game
|
|||||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) =>
|
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) =>
|
||||||
dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
dependencies = new DependencyContainer(base.CreateLocalDependencies(parent));
|
||||||
|
|
||||||
private OsuDbContext createDbContext() => new OsuDbContext(Host.Storage.GetDatabaseConnectionString(@"client"));
|
private DatabaseContextFactory contextFactory;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
|
dependencies.Cache(contextFactory = new DatabaseContextFactory(Host));
|
||||||
|
|
||||||
dependencies.Cache(this);
|
dependencies.Cache(this);
|
||||||
dependencies.Cache(LocalConfig);
|
dependencies.Cache(LocalConfig);
|
||||||
|
|
||||||
using (var dbContext = createDbContext())
|
using (var context = contextFactory.GetContext())
|
||||||
dbContext.Database.Migrate();
|
context.Database.Migrate();
|
||||||
|
|
||||||
dependencies.Cache(API = new APIAccess
|
dependencies.Cache(API = new APIAccess
|
||||||
{
|
{
|
||||||
@ -98,11 +100,11 @@ namespace osu.Game
|
|||||||
Token = LocalConfig.Get<string>(OsuSetting.Token)
|
Token = LocalConfig.Get<string>(OsuSetting.Token)
|
||||||
});
|
});
|
||||||
|
|
||||||
dependencies.Cache(RulesetStore = new RulesetStore(createDbContext()));
|
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory.GetContext));
|
||||||
dependencies.Cache(FileStore = new FileStore(createDbContext(), Host.Storage));
|
dependencies.Cache(FileStore = new FileStore(contextFactory.GetContext, Host.Storage));
|
||||||
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, FileStore, createDbContext(), RulesetStore, API, Host));
|
dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory.GetContext, RulesetStore, API, Host));
|
||||||
dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, createDbContext(), Host, BeatmapManager, RulesetStore));
|
dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory.GetContext, Host, BeatmapManager, RulesetStore));
|
||||||
dependencies.Cache(KeyBindingStore = new KeyBindingStore(createDbContext(), RulesetStore));
|
dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory.GetContext, RulesetStore));
|
||||||
dependencies.Cache(new OsuColour());
|
dependencies.Cache(new OsuColour());
|
||||||
|
|
||||||
//this completely overrides the framework default. will need to change once we make a proper FontStore.
|
//this completely overrides the framework default. will need to change once we make a proper FontStore.
|
||||||
|
@ -26,8 +26,8 @@ namespace osu.Game.Rulesets
|
|||||||
loadRulesetFromFile(file);
|
loadRulesetFromFile(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RulesetStore(OsuDbContext context)
|
public RulesetStore(Func<OsuDbContext> factory)
|
||||||
: base(context)
|
: base(factory)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
@ -25,7 +26,7 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
|
// ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised)
|
||||||
private ScoreIPCChannel ipc;
|
private ScoreIPCChannel ipc;
|
||||||
|
|
||||||
public ScoreStore(Storage storage, OsuDbContext context, IIpcHost importHost = null, BeatmapManager beatmaps = null, RulesetStore rulesets = null) : base(context)
|
public ScoreStore(Storage storage, Func<OsuDbContext> factory, IIpcHost importHost = null, BeatmapManager beatmaps = null, RulesetStore rulesets = null) : base(factory)
|
||||||
{
|
{
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
this.beatmaps = beatmaps;
|
this.beatmaps = beatmaps;
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.IO;
|
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Screens.Select;
|
using osu.Game.Screens.Select;
|
||||||
using osu.Game.Screens.Select.Filter;
|
using osu.Game.Screens.Select.Filter;
|
||||||
@ -25,8 +25,6 @@ namespace osu.Game.Tests.Visual
|
|||||||
|
|
||||||
private DependencyContainer dependencies;
|
private DependencyContainer dependencies;
|
||||||
|
|
||||||
private FileStore files;
|
|
||||||
|
|
||||||
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent);
|
protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(parent);
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
@ -40,9 +38,10 @@ namespace osu.Game.Tests.Visual
|
|||||||
|
|
||||||
var dbConnectionString = storage.GetDatabaseConnectionString(@"client");
|
var dbConnectionString = storage.GetDatabaseConnectionString(@"client");
|
||||||
|
|
||||||
dependencies.Cache(rulesets = new RulesetStore(new OsuDbContext(dbConnectionString)));
|
Func<OsuDbContext> contextFactory = () => new OsuDbContext(dbConnectionString);
|
||||||
dependencies.Cache(files = new FileStore(new OsuDbContext(dbConnectionString), storage));
|
|
||||||
dependencies.Cache(manager = new BeatmapManager(storage, files, new OsuDbContext(dbConnectionString), rulesets, null));
|
dependencies.Cache(rulesets = new RulesetStore(contextFactory));
|
||||||
|
dependencies.Cache(manager = new BeatmapManager(storage, contextFactory, rulesets, null));
|
||||||
|
|
||||||
for (int i = 0; i < 100; i += 10)
|
for (int i = 0; i < 100; i += 10)
|
||||||
manager.Import(createTestBeatmapSet(i));
|
manager.Import(createTestBeatmapSet(i));
|
||||||
|
@ -285,6 +285,7 @@
|
|||||||
<Compile Include="Beatmaps\Drawables\BeatmapPanel.cs" />
|
<Compile Include="Beatmaps\Drawables\BeatmapPanel.cs" />
|
||||||
<Compile Include="Beatmaps\Drawables\BeatmapSetCover.cs" />
|
<Compile Include="Beatmaps\Drawables\BeatmapSetCover.cs" />
|
||||||
<Compile Include="Beatmaps\Drawables\BeatmapSetHeader.cs" />
|
<Compile Include="Beatmaps\Drawables\BeatmapSetHeader.cs" />
|
||||||
|
<Compile Include="Database\DatabaseContextFactory.cs" />
|
||||||
<Compile Include="Migrations\20171014052545_Init.cs" />
|
<Compile Include="Migrations\20171014052545_Init.cs" />
|
||||||
<Compile Include="Migrations\20171014052545_Init.designer.cs">
|
<Compile Include="Migrations\20171014052545_Init.designer.cs">
|
||||||
<DependentUpon>20171014052545_Init.cs</DependentUpon>
|
<DependentUpon>20171014052545_Init.cs</DependentUpon>
|
||||||
|
Loading…
Reference in New Issue
Block a user