move child definitions to db

pull/702/head
Luke Pulverenti 11 years ago
parent 6f15aeccd0
commit 6bc263052d

@ -407,6 +407,18 @@ namespace MediaBrowser.Common.Implementations
} }
} }
/// <summary>
/// Gets the export types.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>IEnumerable{Type}.</returns>
public IEnumerable<Type> GetExportTypes<T>()
{
var currentType = typeof(T);
return AllConcreteTypes.AsParallel().Where(currentType.IsAssignableFrom);
}
/// <summary> /// <summary>
/// Gets the exports. /// Gets the exports.
/// </summary> /// </summary>
@ -415,9 +427,7 @@ namespace MediaBrowser.Common.Implementations
/// <returns>IEnumerable{``0}.</returns> /// <returns>IEnumerable{``0}.</returns>
public IEnumerable<T> GetExports<T>(bool manageLiftime = true) public IEnumerable<T> GetExports<T>(bool manageLiftime = true)
{ {
var currentType = typeof(T); var parts = GetExportTypes<T>().Select(CreateInstance).Cast<T>().ToArray();
var parts = AllConcreteTypes.AsParallel().Where(currentType.IsAssignableFrom).Select(CreateInstance).Cast<T>().ToArray();
if (manageLiftime) if (manageLiftime)
{ {

@ -143,6 +143,7 @@ namespace MediaBrowser.Controller.Entities
public static IServerConfigurationManager ConfigurationManager { get; set; } public static IServerConfigurationManager ConfigurationManager { get; set; }
public static IProviderManager ProviderManager { get; set; } public static IProviderManager ProviderManager { get; set; }
public static ILocalizationManager LocalizationManager { get; set; } public static ILocalizationManager LocalizationManager { get; set; }
public static IItemRepository ItemRepository { get; set; }
/// <summary> /// <summary>
/// Returns a <see cref="System.String" /> that represents this instance. /// Returns a <see cref="System.String" /> that represents this instance.

@ -0,0 +1,22 @@
using System;
namespace MediaBrowser.Controller.Entities
{
/// <summary>
/// Class ChildDefinition
/// </summary>
public class ChildDefinition
{
/// <summary>
/// Gets or sets the item id.
/// </summary>
/// <value>The item id.</value>
public Guid ItemId { get; set; }
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
public string Type { get; set; }
}
}

@ -22,14 +22,7 @@ namespace MediaBrowser.Controller.Entities
/// </summary> /// </summary>
public class Folder : BaseItem public class Folder : BaseItem
{ {
private static TypeMapper _typeMapper = new TypeMapper(); private static readonly TypeMapper _typeMapper = new TypeMapper();
public Folder()
{
ChildDefinitions = new ConcurrentDictionary<Guid, string>();
}
public ConcurrentDictionary<Guid, string> ChildDefinitions { get; set; }
/// <summary> /// <summary>
/// Gets a value indicating whether this instance is folder. /// Gets a value indicating whether this instance is folder.
@ -118,14 +111,19 @@ namespace MediaBrowser.Controller.Entities
item.DateModified = DateTime.Now; item.DateModified = DateTime.Now;
} }
if (!_children.TryAdd(item.Id, item) || !ChildDefinitions.TryAdd(item.Id, item.GetType().FullName)) if (!_children.TryAdd(item.Id, item))
{ {
throw new InvalidOperationException("Unable to add " + item.Name); throw new InvalidOperationException("Unable to add " + item.Name);
} }
await LibraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false); await LibraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
await LibraryManager.UpdateItem(this, cancellationToken).ConfigureAwait(false); await ItemRepository.SaveChildren(Id, _children.Values.ToList().Select(i => new ChildDefinition
{
ItemId = i.Id,
Type = i.GetType().FullName
}), cancellationToken).ConfigureAwait(false);
} }
/// <summary> /// <summary>
@ -153,18 +151,22 @@ namespace MediaBrowser.Controller.Entities
public Task RemoveChild(BaseItem item, CancellationToken cancellationToken) public Task RemoveChild(BaseItem item, CancellationToken cancellationToken)
{ {
BaseItem removed; BaseItem removed;
string removedType;
if (!_children.TryRemove(item.Id, out removed) || !ChildDefinitions.TryRemove(item.Id, out removedType)) if (!_children.TryRemove(item.Id, out removed))
{ {
throw new InvalidOperationException("Unable to remove " + item.Name); throw new InvalidOperationException("Unable to remove " + item.Name);
} }
item.Parent = null; item.Parent = null;
LibraryManager.ReportItemRemoved(item); LibraryManager.ReportItemRemoved(item);
return LibraryManager.UpdateItem(this, cancellationToken); return ItemRepository.SaveChildren(Id, _children.Values.ToList().Select(i => new ChildDefinition
{
ItemId = i.Id,
Type = i.GetType().FullName
}), cancellationToken);
} }
#region Indexing #region Indexing
@ -297,7 +299,7 @@ namespace MediaBrowser.Controller.Entities
.Where(i => i != null) .Where(i => i != null)
.Select(a => new IndexFolder(us, a, .Select(a => new IndexFolder(us, a,
songs.Where(i => string.Equals(i.Artist, a.Name, StringComparison.OrdinalIgnoreCase) songs.Where(i => string.Equals(i.Artist, a.Name, StringComparison.OrdinalIgnoreCase)
), currentIndexName)).Concat(indexFolders); ), currentIndexName)).Concat(indexFolders);
} }
return indexFolders; return indexFolders;
@ -495,7 +497,7 @@ namespace MediaBrowser.Controller.Entities
/// Gets or sets the actual children. /// Gets or sets the actual children.
/// </summary> /// </summary>
/// <value>The actual children.</value> /// <value>The actual children.</value>
protected virtual ConcurrentDictionary<Guid,BaseItem> ActualChildren protected virtual ConcurrentDictionary<Guid, BaseItem> ActualChildren
{ {
get get
{ {
@ -558,10 +560,10 @@ namespace MediaBrowser.Controller.Entities
/// We want this sychronous. /// We want this sychronous.
/// </summary> /// </summary>
/// <returns>ConcurrentBag{BaseItem}.</returns> /// <returns>ConcurrentBag{BaseItem}.</returns>
protected virtual ConcurrentDictionary<Guid,BaseItem> LoadChildren() protected virtual ConcurrentDictionary<Guid, BaseItem> LoadChildren()
{ {
//just load our children from the repo - the library will be validated and maintained in other processes //just load our children from the repo - the library will be validated and maintained in other processes
return new ConcurrentDictionary<Guid,BaseItem>(GetCachedChildren().ToDictionary(i => i.Id)); return new ConcurrentDictionary<Guid, BaseItem>(GetCachedChildren().ToDictionary(i => i.Id));
} }
/// <summary> /// <summary>
@ -709,9 +711,6 @@ namespace MediaBrowser.Controller.Entities
} }
else else
{ {
string removedType;
ChildDefinitions.TryRemove(item.Id, out removedType);
LibraryManager.ReportItemRemoved(item); LibraryManager.ReportItemRemoved(item);
} }
} }
@ -726,13 +725,16 @@ namespace MediaBrowser.Controller.Entities
} }
else else
{ {
ChildDefinitions.TryAdd(item.Id, item.GetType().FullName);
Logger.Debug("** " + item.Name + " Added to library."); Logger.Debug("** " + item.Name + " Added to library.");
} }
} }
await LibraryManager.UpdateItem(this, CancellationToken.None).ConfigureAwait(false); await ItemRepository.SaveChildren(Id, _children.Values.ToList().Select(i => new ChildDefinition
{
ItemId = i.Id,
Type = i.GetType().FullName
}), cancellationToken).ConfigureAwait(false);
//force the indexes to rebuild next time //force the indexes to rebuild next time
IndexCache.Clear(); IndexCache.Clear();
@ -804,7 +806,7 @@ namespace MediaBrowser.Controller.Entities
{ {
lock (percentages) lock (percentages)
{ {
percentages[child.Id] = p/100; percentages[child.Id] = p / 100;
var percent = percentages.Values.Sum(); var percent = percentages.Values.Sum();
percent /= list.Count; percent /= list.Count;
@ -862,7 +864,7 @@ namespace MediaBrowser.Controller.Entities
/// <returns>IEnumerable{BaseItem}.</returns> /// <returns>IEnumerable{BaseItem}.</returns>
protected IEnumerable<BaseItem> GetCachedChildren() protected IEnumerable<BaseItem> GetCachedChildren()
{ {
var items = ChildDefinitions.ToList().Select(RetrieveChild).Where(i => i != null).ToList(); var items = ItemRepository.GetChildren(Id).Select(RetrieveChild).Where(i => i != null).ToList();
foreach (var item in items) foreach (var item in items)
{ {
@ -877,9 +879,9 @@ namespace MediaBrowser.Controller.Entities
/// </summary> /// </summary>
/// <param name="child">The child.</param> /// <param name="child">The child.</param>
/// <returns>BaseItem.</returns> /// <returns>BaseItem.</returns>
private BaseItem RetrieveChild(KeyValuePair<Guid,string> child) private BaseItem RetrieveChild(ChildDefinition child)
{ {
var type = child.Value; var type = child.Type;
var itemType = _typeMapper.GetType(type); var itemType = _typeMapper.GetType(type);
@ -889,7 +891,7 @@ namespace MediaBrowser.Controller.Entities
return null; return null;
} }
var item = LibraryManager.RetrieveItem(child.Key, itemType); var item = LibraryManager.RetrieveItem(child.ItemId, itemType);
return item is IByReferenceItem ? LibraryManager.GetOrAddByReferenceItem(item) : item; return item is IByReferenceItem ? LibraryManager.GetOrAddByReferenceItem(item) : item;
} }

@ -74,6 +74,7 @@
<Compile Include="Dto\SessionInfoDtoBuilder.cs" /> <Compile Include="Dto\SessionInfoDtoBuilder.cs" />
<Compile Include="Entities\Audio\MusicAlbumDisc.cs" /> <Compile Include="Entities\Audio\MusicAlbumDisc.cs" />
<Compile Include="Entities\Audio\MusicGenre.cs" /> <Compile Include="Entities\Audio\MusicGenre.cs" />
<Compile Include="Entities\ChildDefinition.cs" />
<Compile Include="Entities\IByReferenceItem.cs" /> <Compile Include="Entities\IByReferenceItem.cs" />
<Compile Include="Entities\MusicVideo.cs" /> <Compile Include="Entities\MusicVideo.cs" />
<Compile Include="Library\ILibraryPostScanTask.cs" /> <Compile Include="Library\ILibraryPostScanTask.cs" />

@ -10,6 +10,12 @@ namespace MediaBrowser.Controller.Persistence
/// </summary> /// </summary>
public interface IDisplayPreferencesRepository : IRepository public interface IDisplayPreferencesRepository : IRepository
{ {
/// <summary>
/// Opens the connection to the repository
/// </summary>
/// <returns>Task.</returns>
Task Initialize();
/// <summary> /// <summary>
/// Saves display preferences for an item /// Saves display preferences for an item
/// </summary> /// </summary>

@ -13,6 +13,12 @@ namespace MediaBrowser.Controller.Persistence
/// </summary> /// </summary>
public interface IItemRepository : IRepository public interface IItemRepository : IRepository
{ {
/// <summary>
/// Opens the connection to the repository
/// </summary>
/// <returns>Task.</returns>
Task Initialize();
/// <summary> /// <summary>
/// Saves an item /// Saves an item
/// </summary> /// </summary>
@ -75,6 +81,22 @@ namespace MediaBrowser.Controller.Persistence
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
Task SaveChapters(Guid id, IEnumerable<ChapterInfo> chapters, CancellationToken cancellationToken); Task SaveChapters(Guid id, IEnumerable<ChapterInfo> chapters, CancellationToken cancellationToken);
/// <summary>
/// Gets the children.
/// </summary>
/// <param name="parentId">The parent id.</param>
/// <returns>IEnumerable{ChildDefinition}.</returns>
IEnumerable<ChildDefinition> GetChildren(Guid parentId);
/// <summary>
/// Saves the children.
/// </summary>
/// <param name="parentId">The parent id.</param>
/// <param name="children">The children.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SaveChildren(Guid parentId, IEnumerable<ChildDefinition> children, CancellationToken cancellationToken);
} }
/// <summary> /// <summary>

@ -8,12 +8,6 @@ namespace MediaBrowser.Controller.Persistence
/// </summary> /// </summary>
public interface IRepository : IDisposable public interface IRepository : IDisposable
{ {
/// <summary>
/// Opens the connection to the repository
/// </summary>
/// <returns>Task.</returns>
Task Initialize();
/// <summary> /// <summary>
/// Gets the name of the repository /// Gets the name of the repository
/// </summary> /// </summary>

@ -10,6 +10,12 @@ namespace MediaBrowser.Controller.Persistence
/// </summary> /// </summary>
public interface IUserDataRepository : IRepository public interface IUserDataRepository : IRepository
{ {
/// <summary>
/// Opens the connection to the repository
/// </summary>
/// <returns>Task.</returns>
Task Initialize();
/// <summary> /// <summary>
/// Saves the user data. /// Saves the user data.
/// </summary> /// </summary>

@ -10,6 +10,12 @@ namespace MediaBrowser.Controller.Persistence
/// </summary> /// </summary>
public interface IUserRepository : IRepository public interface IUserRepository : IRepository
{ {
/// <summary>
/// Opens the connection to the repository
/// </summary>
/// <returns>Task.</returns>
Task Initialize();
/// <summary> /// <summary>
/// Deletes the user. /// Deletes the user.
/// </summary> /// </summary>

@ -64,6 +64,12 @@
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Common.3.9.54\lib\net35\ServiceStack.Interfaces.dll</HintPath> <HintPath>..\packages\ServiceStack.Common.3.9.54\lib\net35\ServiceStack.Interfaces.dll</HintPath>
</Reference> </Reference>
<Reference Include="ServiceStack.OrmLite">
<HintPath>..\packages\ServiceStack.OrmLite.Sqlite32.3.9.54\lib\net40\ServiceStack.OrmLite.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.OrmLite.SqliteNET">
<HintPath>..\packages\ServiceStack.OrmLite.Sqlite32.3.9.54\lib\net40\ServiceStack.OrmLite.SqliteNET.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.OrmLite.SqlServer, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="ServiceStack.OrmLite.SqlServer, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.OrmLite.SqlServer.3.9.43\lib\ServiceStack.OrmLite.SqlServer.dll</HintPath> <HintPath>..\packages\ServiceStack.OrmLite.SqlServer.3.9.43\lib\ServiceStack.OrmLite.SqlServer.dll</HintPath>

@ -27,16 +27,13 @@ namespace MediaBrowser.Server.Implementations.Persistence
private SQLiteCommand _saveChapterCommand; private SQLiteCommand _saveChapterCommand;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SqliteItemRepository"/> class. /// Initializes a new instance of the <see cref="SqliteItemRepository" /> class.
/// </summary> /// </summary>
/// <param name="appPaths">The app paths.</param> /// <param name="appPaths">The app paths.</param>
/// <param name="jsonSerializer">The json serializer.</param>
/// <param name="logManager">The log manager.</param> /// <param name="logManager">The log manager.</param>
/// <exception cref="System.ArgumentNullException"> /// <exception cref="System.ArgumentNullException">appPaths
/// appPaths
/// or /// or
/// jsonSerializer /// jsonSerializer</exception>
/// </exception>
public SqliteChapterRepository(IApplicationPaths appPaths, ILogManager logManager) public SqliteChapterRepository(IApplicationPaths appPaths, ILogManager logManager)
{ {
if (appPaths == null) if (appPaths == null)

@ -56,6 +56,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
private SqliteChapterRepository _chapterRepository; private SqliteChapterRepository _chapterRepository;
private SQLiteCommand _deleteChildrenCommand;
private SQLiteCommand _saveChildrenCommand;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SqliteItemRepository"/> class. /// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
/// </summary> /// </summary>
@ -95,7 +98,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
public async Task Initialize() public async Task Initialize()
{ {
var dbFile = Path.Combine(_appPaths.DataPath, "library.db"); var dbFile = Path.Combine(_appPaths.DataPath, "library.db");
_connection = await SqliteExtensions.ConnectToDb(dbFile).ConfigureAwait(false); _connection = await SqliteExtensions.ConnectToDb(dbFile).ConfigureAwait(false);
string[] queries = { string[] queries = {
@ -103,6 +106,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
"create table if not exists baseitems (guid GUID primary key, data BLOB)", "create table if not exists baseitems (guid GUID primary key, data BLOB)",
"create index if not exists idx_baseitems on baseitems(guid)", "create index if not exists idx_baseitems on baseitems(guid)",
"create table if not exists ChildDefinitions (ParentId GUID, ItemId GUID, Type TEXT, PRIMARY KEY (ParentId, ItemId))",
"create index if not exists idx_baseitems on baseitems(ParentId,ItemId)",
//pragmas //pragmas
"pragma temp_store = memory" "pragma temp_store = memory"
}; };
@ -131,6 +137,22 @@ namespace MediaBrowser.Server.Implementations.Persistence
_saveItemCommand.Parameters.Add(new SQLiteParameter("@1")); _saveItemCommand.Parameters.Add(new SQLiteParameter("@1"));
_saveItemCommand.Parameters.Add(new SQLiteParameter("@2")); _saveItemCommand.Parameters.Add(new SQLiteParameter("@2"));
_deleteChildrenCommand = new SQLiteCommand
{
CommandText = "delete from ChildDefinitions where ParentId=@ParentId"
};
_deleteChildrenCommand.Parameters.Add(new SQLiteParameter("@ParentId"));
_saveChildrenCommand = new SQLiteCommand
{
CommandText = "replace into ChildDefinitions (ParentId, ItemId, Type) values (@ParentId, @ItemId, @Type)"
};
_saveChildrenCommand.Parameters.Add(new SQLiteParameter("@ParentId"));
_saveChildrenCommand.Parameters.Add(new SQLiteParameter("@ItemId"));
_saveChildrenCommand.Parameters.Add(new SQLiteParameter("@Type"));
} }
/// <summary> /// <summary>
@ -401,5 +423,110 @@ namespace MediaBrowser.Server.Implementations.Persistence
} }
} }
} }
public IEnumerable<ChildDefinition> GetChildren(Guid parentId)
{
if (parentId == Guid.Empty)
{
throw new ArgumentNullException("parentId");
}
using (var cmd = _connection.CreateCommand())
{
cmd.CommandText = "select ItemId,Type from ChildDefinitions where ParentId = @ParentId";
cmd.Parameters.Add("@ParentId", DbType.Guid).Value = parentId;
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
{
while (reader.Read())
{
yield return new ChildDefinition
{
ItemId = reader.GetGuid(0),
Type = reader.GetString(1)
};
}
}
}
}
public async Task SaveChildren(Guid parentId, IEnumerable<ChildDefinition> children, CancellationToken cancellationToken)
{
if (parentId == Guid.Empty)
{
throw new ArgumentNullException("parentId");
}
if (children == null)
{
throw new ArgumentNullException("children");
}
if (cancellationToken == null)
{
throw new ArgumentNullException("cancellationToken");
}
cancellationToken.ThrowIfCancellationRequested();
await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false);
SQLiteTransaction transaction = null;
try
{
transaction = _connection.BeginTransaction();
// First delete
_deleteChildrenCommand.Parameters[0].Value = parentId;
_deleteChildrenCommand.Transaction = transaction;
await _deleteChildrenCommand.ExecuteNonQueryAsync(cancellationToken);
foreach (var chapter in children)
{
cancellationToken.ThrowIfCancellationRequested();
_saveChildrenCommand.Parameters[0].Value = parentId;
_saveChildrenCommand.Parameters[1].Value = chapter.ItemId;
_saveChildrenCommand.Parameters[2].Value = chapter.Type;
_saveChildrenCommand.Transaction = transaction;
await _saveChildrenCommand.ExecuteNonQueryAsync(cancellationToken);
}
transaction.Commit();
}
catch (OperationCanceledException)
{
if (transaction != null)
{
transaction.Rollback();
}
throw;
}
catch (Exception e)
{
_logger.ErrorException("Failed to save children:", e);
if (transaction != null)
{
transaction.Rollback();
}
throw;
}
finally
{
if (transaction != null)
{
transaction.Dispose();
}
_writeLock.Release();
}
}
} }
} }

@ -10,6 +10,7 @@
<package id="ServiceStack" version="3.9.54" targetFramework="net45" /> <package id="ServiceStack" version="3.9.54" targetFramework="net45" />
<package id="ServiceStack.Api.Swagger" version="3.9.54" targetFramework="net45" /> <package id="ServiceStack.Api.Swagger" version="3.9.54" targetFramework="net45" />
<package id="ServiceStack.Common" version="3.9.54" targetFramework="net45" /> <package id="ServiceStack.Common" version="3.9.54" targetFramework="net45" />
<package id="ServiceStack.OrmLite.Sqlite32" version="3.9.54" targetFramework="net45" />
<package id="ServiceStack.OrmLite.SqlServer" version="3.9.43" targetFramework="net45" /> <package id="ServiceStack.OrmLite.SqlServer" version="3.9.43" targetFramework="net45" />
<package id="ServiceStack.Redis" version="3.9.43" targetFramework="net45" /> <package id="ServiceStack.Redis" version="3.9.43" targetFramework="net45" />
<package id="ServiceStack.Text" version="3.9.54" targetFramework="net45" /> <package id="ServiceStack.Text" version="3.9.54" targetFramework="net45" />

@ -109,7 +109,7 @@ namespace MediaBrowser.ServerApplication
return "http://+:" + ServerConfigurationManager.Configuration.HttpServerPortNumber + "/" + WebApplicationName + "/"; return "http://+:" + ServerConfigurationManager.Configuration.HttpServerPortNumber + "/" + WebApplicationName + "/";
} }
} }
/// <summary> /// <summary>
/// Gets the configuration manager. /// Gets the configuration manager.
/// </summary> /// </summary>
@ -359,6 +359,7 @@ namespace MediaBrowser.ServerApplication
BaseItem.LibraryManager = LibraryManager; BaseItem.LibraryManager = LibraryManager;
BaseItem.ProviderManager = ProviderManager; BaseItem.ProviderManager = ProviderManager;
BaseItem.LocalizationManager = LocalizationManager; BaseItem.LocalizationManager = LocalizationManager;
BaseItem.ItemRepository = ItemRepository;
User.XmlSerializer = XmlSerializer; User.XmlSerializer = XmlSerializer;
User.UserManager = UserManager; User.UserManager = UserManager;
LocalizedStrings.ApplicationPaths = ApplicationPaths; LocalizedStrings.ApplicationPaths = ApplicationPaths;
@ -504,7 +505,7 @@ namespace MediaBrowser.ServerApplication
// Include composable parts in the Providers assembly // Include composable parts in the Providers assembly
yield return typeof(ImagesByNameProvider).Assembly; yield return typeof(ImagesByNameProvider).Assembly;
// Common implementations // Common implementations
yield return typeof(TaskManager).Assembly; yield return typeof(TaskManager).Assembly;

Loading…
Cancel
Save