Browse Source

move child definitions to db

Luke Pulverenti 12 năm trước cách đây
mục cha
commit
6bc263052d

+ 13 - 3
MediaBrowser.Common.Implementations/BaseApplicationHost.cs

@@ -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>
         /// Gets the exports.
         /// </summary>
@@ -415,9 +427,7 @@ namespace MediaBrowser.Common.Implementations
         /// <returns>IEnumerable{``0}.</returns>
         public IEnumerable<T> GetExports<T>(bool manageLiftime = true)
         {
-            var currentType = typeof(T);
-
-            var parts = AllConcreteTypes.AsParallel().Where(currentType.IsAssignableFrom).Select(CreateInstance).Cast<T>().ToArray();
+            var parts = GetExportTypes<T>().Select(CreateInstance).Cast<T>().ToArray();
 
             if (manageLiftime)
             {

+ 1 - 0
MediaBrowser.Controller/Entities/BaseItem.cs

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

+ 22 - 0
MediaBrowser.Controller/Entities/ChildDefinition.cs

@@ -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; }
+    }
+}

+ 31 - 29
MediaBrowser.Controller/Entities/Folder.cs

@@ -22,14 +22,7 @@ namespace MediaBrowser.Controller.Entities
     /// </summary>
     public class Folder : BaseItem
     {
-        private static TypeMapper _typeMapper = new TypeMapper();
-
-        public Folder()
-        {
-            ChildDefinitions = new ConcurrentDictionary<Guid, string>();
-        }
-
-        public ConcurrentDictionary<Guid, string> ChildDefinitions { get; set; }
+        private static readonly TypeMapper _typeMapper = new TypeMapper();
 
         /// <summary>
         /// Gets a value indicating whether this instance is folder.
@@ -118,14 +111,19 @@ namespace MediaBrowser.Controller.Entities
                 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);
             }
 
             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>
@@ -153,18 +151,22 @@ namespace MediaBrowser.Controller.Entities
         public Task RemoveChild(BaseItem item, CancellationToken cancellationToken)
         {
             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);
             }
 
             item.Parent = null;
-            
+
             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
@@ -297,7 +299,7 @@ namespace MediaBrowser.Controller.Entities
                     .Where(i => i != null)
                     .Select(a => new IndexFolder(us, a,
                                         songs.Where(i => string.Equals(i.Artist, a.Name, StringComparison.OrdinalIgnoreCase)
-                                        ), currentIndexName)).Concat(indexFolders); 
+                                        ), currentIndexName)).Concat(indexFolders);
                 }
 
                 return indexFolders;
@@ -495,7 +497,7 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the actual children.
         /// </summary>
         /// <value>The actual children.</value>
-        protected virtual ConcurrentDictionary<Guid,BaseItem> ActualChildren
+        protected virtual ConcurrentDictionary<Guid, BaseItem> ActualChildren
         {
             get
             {
@@ -558,10 +560,10 @@ namespace MediaBrowser.Controller.Entities
         /// We want this sychronous.
         /// </summary>
         /// <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
-            return new ConcurrentDictionary<Guid,BaseItem>(GetCachedChildren().ToDictionary(i => i.Id));
+            return new ConcurrentDictionary<Guid, BaseItem>(GetCachedChildren().ToDictionary(i => i.Id));
         }
 
         /// <summary>
@@ -709,9 +711,6 @@ namespace MediaBrowser.Controller.Entities
                     }
                     else
                     {
-                        string removedType;
-                        ChildDefinitions.TryRemove(item.Id, out removedType);
-
                         LibraryManager.ReportItemRemoved(item);
                     }
                 }
@@ -726,13 +725,16 @@ namespace MediaBrowser.Controller.Entities
                     }
                     else
                     {
-                        ChildDefinitions.TryAdd(item.Id, item.GetType().FullName);
-                        
                         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
                 IndexCache.Clear();
@@ -804,7 +806,7 @@ namespace MediaBrowser.Controller.Entities
                         {
                             lock (percentages)
                             {
-                                percentages[child.Id] = p/100;
+                                percentages[child.Id] = p / 100;
 
                                 var percent = percentages.Values.Sum();
                                 percent /= list.Count;
@@ -862,7 +864,7 @@ namespace MediaBrowser.Controller.Entities
         /// <returns>IEnumerable{BaseItem}.</returns>
         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)
             {
@@ -877,9 +879,9 @@ namespace MediaBrowser.Controller.Entities
         /// </summary>
         /// <param name="child">The child.</param>
         /// <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);
 
@@ -889,7 +891,7 @@ namespace MediaBrowser.Controller.Entities
                 return null;
             }
 
-            var item = LibraryManager.RetrieveItem(child.Key, itemType);
+            var item = LibraryManager.RetrieveItem(child.ItemId, itemType);
 
             return item is IByReferenceItem ? LibraryManager.GetOrAddByReferenceItem(item) : item;
         }

+ 1 - 0
MediaBrowser.Controller/MediaBrowser.Controller.csproj

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

+ 6 - 0
MediaBrowser.Controller/Persistence/IDisplayPreferencesRepository.cs

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

+ 22 - 0
MediaBrowser.Controller/Persistence/IItemRepository.cs

@@ -13,6 +13,12 @@ namespace MediaBrowser.Controller.Persistence
     /// </summary>
     public interface IItemRepository : IRepository
     {
+        /// <summary>
+        /// Opens the connection to the repository
+        /// </summary>
+        /// <returns>Task.</returns>
+        Task Initialize();
+
         /// <summary>
         /// Saves an item
         /// </summary>
@@ -75,6 +81,22 @@ namespace MediaBrowser.Controller.Persistence
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         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>

+ 0 - 6
MediaBrowser.Controller/Persistence/IRepository.cs

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

+ 6 - 0
MediaBrowser.Controller/Persistence/IUserDataRepository.cs

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

+ 6 - 0
MediaBrowser.Controller/Persistence/IUserRepository.cs

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

+ 6 - 0
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -64,6 +64,12 @@
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\packages\ServiceStack.Common.3.9.54\lib\net35\ServiceStack.Interfaces.dll</HintPath>
     </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">
       <SpecificVersion>False</SpecificVersion>
       <HintPath>..\packages\ServiceStack.OrmLite.SqlServer.3.9.43\lib\ServiceStack.OrmLite.SqlServer.dll</HintPath>

+ 3 - 6
MediaBrowser.Server.Implementations/Persistence/SqliteChapterRepository.cs

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

+ 128 - 1
MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs

@@ -56,6 +56,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
 
         private SqliteChapterRepository _chapterRepository;
 
+        private SQLiteCommand _deleteChildrenCommand;
+        private SQLiteCommand _saveChildrenCommand;
+        
         /// <summary>
         /// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
         /// </summary>
@@ -95,7 +98,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
         public async Task Initialize()
         {
             var dbFile = Path.Combine(_appPaths.DataPath, "library.db");
-
+            
             _connection = await SqliteExtensions.ConnectToDb(dbFile).ConfigureAwait(false);
 
             string[] queries = {
@@ -103,6 +106,9 @@ namespace MediaBrowser.Server.Implementations.Persistence
                                 "create table if not exists baseitems (guid GUID primary key, data BLOB)",
                                 "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
                                 "pragma temp_store = memory"
                                };
@@ -131,6 +137,22 @@ namespace MediaBrowser.Server.Implementations.Persistence
 
             _saveItemCommand.Parameters.Add(new SQLiteParameter("@1"));
             _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>
@@ -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();
+            }
+        }
     }
 }

+ 1 - 0
MediaBrowser.Server.Implementations/packages.config

@@ -10,6 +10,7 @@
   <package id="ServiceStack" 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.OrmLite.Sqlite32" version="3.9.54" 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.Text" version="3.9.54" targetFramework="net45" />

+ 3 - 2
MediaBrowser.ServerApplication/ApplicationHost.cs

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