2017-04-17 13:37:52 +08:00
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System ;
2017-10-25 21:07:32 +08:00
using System.Collections.Generic ;
using System.Linq ;
2017-10-23 16:56:04 +08:00
using System.Threading ;
2017-10-25 21:07:32 +08:00
using Microsoft.EntityFrameworkCore ;
2017-07-26 19:22:02 +08:00
using osu.Framework.Platform ;
2017-04-17 13:37:52 +08:00
namespace osu.Game.Database
{
2017-07-27 15:56:41 +08:00
public abstract class DatabaseBackedStore
2017-04-17 13:37:52 +08:00
{
2017-07-26 19:22:02 +08:00
protected readonly Storage Storage ;
2017-04-17 13:37:52 +08:00
2017-10-23 15:35:35 +08:00
/// <summary>
/// Create a new <see cref="OsuDbContext"/> instance (separate from the shared context via <see cref="GetContext"/> for performing isolated operations.
/// </summary>
protected readonly Func < OsuDbContext > CreateContext ;
2017-10-17 14:00:27 +08:00
2017-10-23 16:56:04 +08:00
private readonly ThreadLocal < OsuDbContext > queryContext ;
2017-10-23 15:35:35 +08:00
2017-10-25 17:28:28 +08:00
/// <summary>
2017-10-25 21:07:32 +08:00
/// Refresh an instance potentially from a different thread with a local context-tracked instance.
2017-10-25 17:28:28 +08:00
/// </summary>
2017-10-25 21:19:47 +08:00
/// <param name="obj">The object to use as a reference when negotiating a local instance.</param>
/// <param name="lookupSource">An optional lookup source which will be used to query and populate a freshly retrieved replacement. If not provided, the refreshed object will still be returned but will not have any includes.</param>
/// <typeparam name="T">A valid EF-stored type.</typeparam>
2017-10-25 21:07:32 +08:00
protected virtual void Refresh < T > ( ref T obj , IEnumerable < T > lookupSource = null ) where T : class , IHasPrimaryKey
{
var context = GetContext ( ) ;
if ( context . Entry ( obj ) . State ! = EntityState . Detached ) return ;
var id = obj . ID ;
2017-10-25 21:17:17 +08:00
obj = lookupSource ? . SingleOrDefault ( t = > t . ID = = id ) ? ? context . Find < T > ( id ) ;
2017-11-23 16:03:56 +08:00
context . Entry ( obj ) . Reload ( ) ;
2017-10-25 21:07:32 +08:00
}
2017-10-25 17:28:28 +08:00
2017-10-23 15:35:35 +08:00
/// <summary>
/// Retrieve a shared context for performing lookups (or write operations on the update thread, for now).
/// </summary>
protected OsuDbContext GetContext ( ) = > queryContext . Value ;
protected DatabaseBackedStore ( Func < OsuDbContext > createContext , Storage storage = null )
2017-04-17 13:37:52 +08:00
{
2017-10-23 15:35:35 +08:00
CreateContext = createContext ;
2017-10-23 16:56:04 +08:00
// todo: while this seems to work quite well, we need to consider that contexts could enter a state where they are never cleaned up.
queryContext = new ThreadLocal < OsuDbContext > ( CreateContext ) ;
2017-10-23 15:35:35 +08:00
2017-07-26 19:22:02 +08:00
Storage = storage ;
2017-04-17 13:37:52 +08:00
}
/// <summary>
2017-10-17 18:58:33 +08:00
/// Perform any common clean-up tasks. Should be run when idle, or whenever necessary.
2017-08-01 09:08:05 +08:00
/// </summary>
2017-10-17 18:58:33 +08:00
public virtual void Cleanup ( )
2017-08-01 09:08:05 +08:00
{
}
2017-04-17 13:37:52 +08:00
}
2017-07-27 14:06:10 +08:00
}