diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index e977c2c551..a74516fa29 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -241,12 +241,15 @@ namespace MediaBrowser.Api.UserLibrary /// IEnumerable{BaseItem}. private IEnumerable ApplyFilter(IEnumerable items, ItemFilter filter, User user) { + // Avoids implicitly captured closure + var currentUser = user; + switch (filter) { case ItemFilter.Likes: return items.Where(item => { - var userdata = item.GetUserData(user, false); + var userdata = _userManager.GetUserData(user.Id, item.UserDataId).Result; return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value; }); @@ -254,7 +257,7 @@ namespace MediaBrowser.Api.UserLibrary case ItemFilter.Dislikes: return items.Where(item => { - var userdata = item.GetUserData(user, false); + var userdata = _userManager.GetUserData(user.Id, item.UserDataId).Result; return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value; }); @@ -262,18 +265,18 @@ namespace MediaBrowser.Api.UserLibrary case ItemFilter.IsFavorite: return items.Where(item => { - var userdata = item.GetUserData(user, false); + var userdata = _userManager.GetUserData(user.Id, item.UserDataId).Result; return userdata != null && userdata.IsFavorite; }); case ItemFilter.IsRecentlyAdded: - return items.Where(item => item.IsRecentlyAdded(user)); + return items.Where(item => item.IsRecentlyAdded(currentUser)); case ItemFilter.IsResumable: return items.Where(item => { - var userdata = item.GetUserData(user, false); + var userdata = _userManager.GetUserData(user.Id, item.UserDataId).Result; return userdata != null && userdata.PlaybackPositionTicks > 0; }); @@ -281,7 +284,7 @@ namespace MediaBrowser.Api.UserLibrary case ItemFilter.IsPlayed: return items.Where(item => { - var userdata = item.GetUserData(user, false); + var userdata = _userManager.GetUserData(user.Id, item.UserDataId).Result; return userdata != null && userdata.PlayCount > 0; }); @@ -289,7 +292,7 @@ namespace MediaBrowser.Api.UserLibrary case ItemFilter.IsUnplayed: return items.Where(item => { - var userdata = item.GetUserData(user, false); + var userdata = _userManager.GetUserData(user.Id, item.UserDataId).Result; return userdata == null || userdata.PlayCount == 0; }); diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index 7afeef9334..d1f963a9ee 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -503,12 +503,12 @@ namespace MediaBrowser.Api.UserLibrary var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id); // Get the user data for this item - var data = item.GetUserData(user, true); + var data = _userManager.GetUserData(user.Id, item.UserDataId).Result; // Set favorite status data.IsFavorite = true; - var task = _userManager.SaveUserDataForItem(user, item, data); + var task = _userManager.SaveUserData(user.Id, item.UserDataId, data, CancellationToken.None); Task.WaitAll(task); } @@ -524,12 +524,12 @@ namespace MediaBrowser.Api.UserLibrary var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id); // Get the user data for this item - var data = item.GetUserData(user, true); + var data = _userManager.GetUserData(user.Id, item.UserDataId).Result; // Set favorite status data.IsFavorite = false; - var task = _userManager.SaveUserDataForItem(user, item, data); + var task = _userManager.SaveUserData(user.Id, item.UserDataId, data, CancellationToken.None); Task.WaitAll(task); } @@ -545,11 +545,11 @@ namespace MediaBrowser.Api.UserLibrary var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id); // Get the user data for this item - var data = item.GetUserData(user, true); + var data = _userManager.GetUserData(user.Id, item.UserDataId).Result; data.Rating = null; - var task = _userManager.SaveUserDataForItem(user, item, data); + var task = _userManager.SaveUserData(user.Id, item.UserDataId, data, CancellationToken.None); Task.WaitAll(task); } @@ -565,11 +565,11 @@ namespace MediaBrowser.Api.UserLibrary var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id); // Get the user data for this item - var data = item.GetUserData(user, true); + var data = _userManager.GetUserData(user.Id, item.UserDataId).Result; data.Likes = request.Likes; - var task = _userManager.SaveUserDataForItem(user, item, data); + var task = _userManager.SaveUserData(user.Id, item.UserDataId, data, CancellationToken.None); Task.WaitAll(task); } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index d30f11f7d9..411190d77a 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -796,109 +796,6 @@ namespace MediaBrowser.Controller.Entities get { return null; } } - /// - /// The _user data - /// - private IEnumerable _userData; - /// - /// The _user data initialized - /// - private bool _userDataInitialized; - /// - /// The _user data sync lock - /// - private object _userDataSyncLock = new object(); - /// - /// Gets the user data. - /// - /// The user data. - [IgnoreDataMember] - public IEnumerable UserData - { - get - { - // Call ToList to exhaust the stream because we'll be iterating over this multiple times - LazyInitializer.EnsureInitialized(ref _userData, ref _userDataInitialized, ref _userDataSyncLock, () => Kernel.Instance.UserDataRepository.RetrieveUserData(this).ToList()); - return _userData; - } - private set - { - _userData = value; - - if (value == null) - { - _userDataInitialized = false; - } - } - } - - /// - /// Gets the user data. - /// - /// The user. - /// if set to true [create if null]. - /// UserItemData. - /// - public UserItemData GetUserData(User user, bool createIfNull) - { - if (user == null) - { - throw new ArgumentNullException(); - } - - if (UserData == null) - { - if (!createIfNull) - { - return null; - } - - AddOrUpdateUserData(user, new UserItemData { UserId = user.Id }); - } - - var data = UserData.FirstOrDefault(u => u.UserId == user.Id); - - if (data == null && createIfNull) - { - data = new UserItemData { UserId = user.Id }; - AddOrUpdateUserData(user, data); - } - - return data; - } - - /// - /// Adds the or update user data. - /// - /// The user. - /// The data. - /// - public void AddOrUpdateUserData(User user, UserItemData data) - { - if (user == null) - { - throw new ArgumentNullException(); - } - - if (data == null) - { - throw new ArgumentNullException(); - } - - data.UserId = user.Id; - - if (UserData == null) - { - UserData = new[] { data }; - } - else - { - var list = UserData.Where(u => u.UserId != user.Id).ToList(); - list.Add(data); - UserData = list; - } - } - /// /// The _user data id /// @@ -1205,16 +1102,17 @@ namespace MediaBrowser.Controller.Entities /// /// The user. /// if set to true [was played]. + /// The user manager. /// Task. /// - public virtual Task SetPlayedStatus(User user, bool wasPlayed, IUserManager userManager) + public virtual async Task SetPlayedStatus(User user, bool wasPlayed, IUserManager userManager) { if (user == null) { throw new ArgumentNullException(); } - var data = GetUserData(user, true); + var data = await userManager.GetUserData(user.Id, UserDataId).ConfigureAwait(false); if (wasPlayed) { @@ -1237,7 +1135,7 @@ namespace MediaBrowser.Controller.Entities data.Played = wasPlayed; - return userManager.SaveUserDataForItem(user, this, data); + await userManager.SaveUserData(user.Id, UserDataId, data, CancellationToken.None).ConfigureAwait(false); } /// diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index ce4d629257..05ee87defa 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -799,6 +799,7 @@ namespace MediaBrowser.Controller.Entities /// /// The user. /// if set to true [was played]. + /// The user manager. /// Task. public override async Task SetPlayedStatus(User user, bool wasPlayed, IUserManager userManager) { diff --git a/MediaBrowser.Controller/Library/DtoBuilder.cs b/MediaBrowser.Controller/Library/DtoBuilder.cs index 419d3527d5..d9244c64c2 100644 --- a/MediaBrowser.Controller/Library/DtoBuilder.cs +++ b/MediaBrowser.Controller/Library/DtoBuilder.cs @@ -167,12 +167,9 @@ namespace MediaBrowser.Controller.Library { if (fields.Contains(ItemFields.UserData)) { - var userData = item.GetUserData(user, false); + var userData = await _userManager.GetUserData(user.Id, item.UserDataId).ConfigureAwait(false); - if (userData != null) - { - dto.UserData = GetUserItemDataDto(userData); - } + dto.UserData = GetUserItemDataDto(userData); } if (item.IsFolder && fields.Contains(ItemFields.DisplayPreferences)) @@ -191,7 +188,7 @@ namespace MediaBrowser.Controller.Library // Skip sorting since all we want is a count dto.ChildCount = folder.GetChildren(user).Count(); - SetSpecialCounts(folder, user, dto); + await SetSpecialCounts(folder, user, dto, _userManager).ConfigureAwait(false); } } } @@ -488,7 +485,9 @@ namespace MediaBrowser.Controller.Library /// The folder. /// The user. /// The dto. - private static void SetSpecialCounts(Folder folder, User user, BaseItemDto dto) + /// The user manager. + /// Task. + private static async Task SetSpecialCounts(Folder folder, User user, BaseItemDto dto, IUserManager userManager) { var rcentlyAddedItemCount = 0; var recursiveItemCount = 0; @@ -498,7 +497,7 @@ namespace MediaBrowser.Controller.Library // Loop through each recursive child foreach (var child in folder.GetRecursiveChildren(user).Where(i => !i.IsFolder)) { - var userdata = child.GetUserData(user, false); + var userdata = await userManager.GetUserData(user.Id, child.UserDataId).ConfigureAwait(false); recursiveItemCount++; diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index 0e93c15cfb..8374c6880b 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -1,11 +1,11 @@ using MediaBrowser.Common.Events; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Connectivity; +using MediaBrowser.Model.Entities; using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Library { @@ -157,14 +157,6 @@ namespace MediaBrowser.Controller.Library /// Task OnPlaybackStopped(User user, BaseItem item, long? positionTicks, string clientType, string deviceId, string deviceName); - /// - /// Saves user data for an item - /// - /// The user. - /// The item. - /// The data. - Task SaveUserDataForItem(User user, BaseItem item, UserItemData data); - /// /// Resets the password. /// @@ -180,6 +172,25 @@ namespace MediaBrowser.Controller.Library /// Task. Task ChangePassword(User user, string newPassword); + /// + /// Saves display preferences for an item + /// + /// The user id. + /// The user data id. + /// The user data. + /// The cancellation token. + /// Task. + Task SaveUserData(Guid userId, Guid userDataId, UserItemData userData, + CancellationToken cancellationToken); + + /// + /// Gets the display preferences. + /// + /// The user id. + /// The user data id. + /// Task{DisplayPreferences}. + Task GetUserData(Guid userId, Guid userDataId); + /// /// Gets the display preferences. /// diff --git a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs index 115bd411ab..592206faf9 100644 --- a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs +++ b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs @@ -1,5 +1,5 @@ using MediaBrowser.Controller.Entities; -using System.Collections.Generic; +using System; using System.Threading; using System.Threading.Tasks; @@ -11,18 +11,22 @@ namespace MediaBrowser.Controller.Persistence public interface IUserDataRepository : IRepository { /// - /// Saves user data for an item + /// Saves the user data. /// - /// The item. + /// The user id. + /// The user data id. + /// The user data. /// The cancellation token. /// Task. - Task SaveUserData(BaseItem item, CancellationToken cancellationToken); + Task SaveUserData(Guid userId, Guid userDataId, UserItemData userData, + CancellationToken cancellationToken); /// - /// Gets user data for an item + /// Gets the user data. /// - /// The item. - /// IEnumerable{UserItemData}. - IEnumerable RetrieveUserData(BaseItem item); + /// The user id. + /// The user data id. + /// Task{UserItemData}. + Task GetUserData(Guid userId, Guid userDataId); } } diff --git a/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs b/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs index b9da1f6a62..76d84a653d 100644 --- a/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs +++ b/MediaBrowser.Controller/Sorting/IUserBaseItemComparer.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; namespace MediaBrowser.Controller.Sorting { @@ -12,5 +13,11 @@ namespace MediaBrowser.Controller.Sorting /// /// The user. User User { get; set; } + + /// + /// Gets or sets the user manager. + /// + /// The user manager. + IUserManager UserManager { get; set; } } } diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index 0c1d2ddf82..a57eb8eaa3 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -861,6 +861,7 @@ namespace MediaBrowser.Server.Implementations.Library var userComparer = (IUserBaseItemComparer)Activator.CreateInstance(comparer.GetType()); userComparer.User = user; + userComparer.UserManager = _userManager; return userComparer; } diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs index b17f2955ee..ca3c1fe7cf 100644 --- a/MediaBrowser.Server.Implementations/Library/UserManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs @@ -1,5 +1,4 @@ -using System.Collections.Concurrent; -using MediaBrowser.Common.Events; +using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; @@ -9,6 +8,7 @@ using MediaBrowser.Model.Connectivity; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; @@ -104,6 +104,8 @@ namespace MediaBrowser.Server.Implementations.Library /// private readonly ConcurrentDictionary> _displayPreferences = new ConcurrentDictionary>(); + private readonly ConcurrentDictionary> _userData = new ConcurrentDictionary>(); + /// /// Initializes a new instance of the class. /// @@ -200,14 +202,15 @@ namespace MediaBrowser.Server.Implementations.Library public async Task SaveDisplayPreferences(Guid userId, Guid displayPreferencesId, DisplayPreferences displayPreferences, CancellationToken cancellationToken) { var key = userId + displayPreferencesId.ToString(); - var newValue = Task.FromResult(displayPreferences); - + try { await Kernel.Instance.DisplayPreferencesRepository.SaveDisplayPreferences(userId, displayPreferencesId, displayPreferences, cancellationToken).ConfigureAwait(false); + var newValue = Task.FromResult(displayPreferences); + // Once it succeeds, put it into the dictionary to make it available to everyone else _displayPreferences.AddOrUpdate(key, newValue, delegate { return newValue; }); } @@ -651,10 +654,10 @@ namespace MediaBrowser.Server.Implementations.Library if (positionTicks.HasValue) { - var data = item.GetUserData(user, true); + var data = await GetUserData(user.Id, item.UserDataId).ConfigureAwait(false); UpdatePlayState(item, data, positionTicks.Value, false); - await SaveUserDataForItem(user, item, data).ConfigureAwait(false); + await SaveUserData(user.Id, item.UserDataId, data, CancellationToken.None).ConfigureAwait(false); } EventHelper.QueueEventIfNotNull(PlaybackProgress, this, new PlaybackProgressEventArgs @@ -689,7 +692,7 @@ namespace MediaBrowser.Server.Implementations.Library RemoveNowPlayingItemId(user, clientType, deviceId, deviceName, item); - var data = item.GetUserData(user, true); + var data = await GetUserData(user.Id, item.UserDataId).ConfigureAwait(false); if (positionTicks.HasValue) { @@ -702,7 +705,7 @@ namespace MediaBrowser.Server.Implementations.Library data.Played = true; } - await SaveUserDataForItem(user, item, data).ConfigureAwait(false); + await SaveUserData(user.Id, item.UserDataId, data, CancellationToken.None).ConfigureAwait(false); EventHelper.QueueEventIfNotNull(PlaybackStopped, this, new PlaybackProgressEventArgs { @@ -763,16 +766,57 @@ namespace MediaBrowser.Server.Implementations.Library } /// - /// Saves user data for an item + /// Saves display preferences for an item /// - /// The user. - /// The item. - /// The data. - public Task SaveUserDataForItem(User user, BaseItem item, UserItemData data) + /// The user id. + /// The user data id. + /// The user data. + /// The cancellation token. + /// Task. + public async Task SaveUserData(Guid userId, Guid userDataId, UserItemData userData, CancellationToken cancellationToken) { - item.AddOrUpdateUserData(user, data); + var key = userId + userDataId.ToString(); + try + { + await Kernel.Instance.UserDataRepository.SaveUserData(userId, userDataId, userData, cancellationToken).ConfigureAwait(false); - return Kernel.UserDataRepository.SaveUserData(item, CancellationToken.None); + var newValue = Task.FromResult(userData); + + // Once it succeeds, put it into the dictionary to make it available to everyone else + _userData.AddOrUpdate(key, newValue, delegate { return newValue; }); + } + catch (Exception ex) + { + _logger.ErrorException("Error saving user data", ex); + + throw; + } + } + + /// + /// Gets the display preferences. + /// + /// The user id. + /// The user data id. + /// Task{DisplayPreferences}. + public Task GetUserData(Guid userId, Guid userDataId) + { + var key = userId + userDataId.ToString(); + + return _userData.GetOrAdd(key, keyName => RetrieveUserData(userId, userDataId)); + } + + /// + /// Retrieves the display preferences. + /// + /// The user id. + /// The user data id. + /// DisplayPreferences. + private async Task RetrieveUserData(Guid userId, Guid userDataId) + { + var userdata = await Kernel.Instance.UserDataRepository.GetUserData(userId, userDataId).ConfigureAwait(false); + + return userdata ?? new UserItemData(); } } } diff --git a/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs b/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs index c37b6b4a47..905d5413a7 100644 --- a/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs +++ b/MediaBrowser.Server.Implementations/Sorting/DatePlayedComparer.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Sorting; using MediaBrowser.Model.Querying; using System; @@ -16,6 +17,12 @@ namespace MediaBrowser.Server.Implementations.Sorting /// The user. public User User { get; set; } + /// + /// Gets or sets the user manager. + /// + /// The user manager. + public IUserManager UserManager { get; set; } + /// /// Compares the specified x. /// @@ -34,7 +41,7 @@ namespace MediaBrowser.Server.Implementations.Sorting /// DateTime. private DateTime GetDate(BaseItem x) { - var userdata = x.GetUserData(User, false); + var userdata = UserManager.GetUserData(User.Id, x.UserDataId).Result; if (userdata != null && userdata.LastPlayedDate.HasValue) { diff --git a/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs b/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs index 9cd5281c36..82e76e78d4 100644 --- a/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs +++ b/MediaBrowser.Server.Implementations/Sorting/PlayCountComparer.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Sorting; using MediaBrowser.Model.Querying; @@ -33,7 +34,7 @@ namespace MediaBrowser.Server.Implementations.Sorting /// DateTime. private int GetValue(BaseItem x) { - var userdata = x.GetUserData(User, false); + var userdata = UserManager.GetUserData(User.Id, x.UserDataId).Result; return userdata == null ? 0 : userdata.PlayCount; } @@ -46,5 +47,11 @@ namespace MediaBrowser.Server.Implementations.Sorting { get { return ItemSortBy.PlayCount; } } + + /// + /// Gets or sets the user manager. + /// + /// The user manager. + public IUserManager UserManager { get; set; } } } diff --git a/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs b/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs index dafdce798f..f3dede889d 100644 --- a/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs +++ b/MediaBrowser.Server.Implementations/Sqlite/SQLiteUserDataRepository.cs @@ -79,8 +79,8 @@ namespace MediaBrowser.Server.Implementations.Sqlite string[] queries = { - "create table if not exists user_data (item_id GUID, user_id GUID, data BLOB)", - "create unique index if not exists idx_user_data on user_data (item_id, user_id)", + "create table if not exists userdata (id GUID, userId GUID, data BLOB)", + "create unique index if not exists userdataindex on userdata (id, userId)", "create table if not exists schema_version (table_name primary key, version)", //pragmas "pragma temp_store = memory" @@ -90,84 +90,101 @@ namespace MediaBrowser.Server.Implementations.Sqlite } /// - /// Save the user specific data associated with an item in the repo + /// Saves the user data. /// - /// The item. + /// The user id. + /// The user data id. + /// The user data. /// The cancellation token. /// Task. - /// item - public Task SaveUserData(BaseItem item, CancellationToken cancellationToken) + /// + /// userData + /// or + /// cancellationToken + /// or + /// userId + /// or + /// userDataId + /// + public Task SaveUserData(Guid userId, Guid userDataId, UserItemData userData, CancellationToken cancellationToken) { - if (item == null) + if (userData == null) { - throw new ArgumentNullException("item"); + throw new ArgumentNullException("userData"); } - if (cancellationToken == null) { throw new ArgumentNullException("cancellationToken"); } - + if (userId == Guid.Empty) + { + throw new ArgumentNullException("userId"); + } + if (userDataId == Guid.Empty) + { + throw new ArgumentNullException("userDataId"); + } + + cancellationToken.ThrowIfCancellationRequested(); + return Task.Run(() => { + var serialized = _protobufSerializer.SerializeToBytes(userData); + cancellationToken.ThrowIfCancellationRequested(); var cmd = connection.CreateCommand(); - - cmd.CommandText = "delete from user_data where item_id = @guid"; - cmd.AddParam("@guid", item.UserDataId); - + cmd.CommandText = "replace into userdata (id, userId, data) values (@1, @2, @3)"; + cmd.AddParam("@1", userDataId); + cmd.AddParam("@2", userId); + cmd.AddParam("@3", serialized); QueueCommand(cmd); - - if (item.UserData != null) - { - foreach (var data in item.UserData) - { - cmd = connection.CreateCommand(); - cmd.CommandText = "insert into user_data (item_id, user_id, data) values (@1, @2, @3)"; - cmd.AddParam("@1", item.UserDataId); - cmd.AddParam("@2", data.UserId); - - cmd.AddParam("@3", _protobufSerializer.SerializeToBytes(data)); - - QueueCommand(cmd); - } - } }); } /// - /// Gets user data for an item + /// Gets the user data. /// - /// The item. - /// IEnumerable{UserItemData}. - /// item - public IEnumerable RetrieveUserData(BaseItem item) + /// The user id. + /// The user data id. + /// Task{UserItemData}. + /// + /// userId + /// or + /// userDataId + /// + public async Task GetUserData(Guid userId, Guid userDataId) { - if (item == null) + if (userId == Guid.Empty) { - throw new ArgumentNullException("item"); + throw new ArgumentNullException("userId"); + } + if (userDataId == Guid.Empty) + { + throw new ArgumentNullException("userDataId"); } var cmd = connection.CreateCommand(); - cmd.CommandText = "select data from user_data where item_id = @guid"; - var guidParam = cmd.Parameters.Add("@guid", DbType.Guid); - guidParam.Value = item.UserDataId; + cmd.CommandText = "select data from userdata where id = @id and userId=@userId"; - using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) + var idParam = cmd.Parameters.Add("@id", DbType.Guid); + idParam.Value = userDataId; + + var userIdParam = cmd.Parameters.Add("@userId", DbType.Guid); + userIdParam.Value = userId; + + using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow).ConfigureAwait(false)) { - while (reader.Read()) + if (reader.Read()) { using (var stream = GetStream(reader, 0)) { - var data = _protobufSerializer.DeserializeFromStream(stream); - if (data != null) - { - yield return data; - } + return _protobufSerializer.DeserializeFromStream(stream); } } } + + return null; } } }