using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; using System; using System.Collections.Generic; using System.Data; using System.IO; using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Server.Implementations.Sqlite { /// /// Class SQLiteUserDataRepository /// public class SQLiteUserDataRepository : SqliteRepository, IUserDataRepository { /// /// The repository name /// public const string RepositoryName = "SQLite"; /// /// Gets the name of the repository /// /// The name. public string Name { get { return RepositoryName; } } /// /// Gets a value indicating whether [enable delayed commands]. /// /// true if [enable delayed commands]; otherwise, false. protected override bool EnableDelayedCommands { get { return false; } } /// /// The _protobuf serializer /// private readonly IProtobufSerializer _protobufSerializer; /// /// The _app paths /// private readonly IApplicationPaths _appPaths; /// /// Initializes a new instance of the class. /// /// The app paths. /// The protobuf serializer. /// The log manager. /// protobufSerializer public SQLiteUserDataRepository(IApplicationPaths appPaths, IProtobufSerializer protobufSerializer, ILogManager logManager) : base(logManager) { if (protobufSerializer == null) { throw new ArgumentNullException("protobufSerializer"); } if (appPaths == null) { throw new ArgumentNullException("appPaths"); } _protobufSerializer = protobufSerializer; _appPaths = appPaths; } /// /// Opens the connection to the database /// /// Task. public async Task Initialize() { var dbFile = Path.Combine(_appPaths.DataPath, "userdata.db"); await ConnectToDB(dbFile).ConfigureAwait(false); string[] queries = { "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" }; RunQueries(queries); } /// /// Saves the user data. /// /// The user id. /// The user data id. /// The user data. /// The cancellation token. /// Task. /// /// userData /// or /// cancellationToken /// or /// userId /// or /// userDataId /// public async Task SaveUserData(Guid userId, Guid userDataId, UserItemData userData, CancellationToken cancellationToken) { if (userData == null) { 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(); var serialized = _protobufSerializer.SerializeToBytes(userData); cancellationToken.ThrowIfCancellationRequested(); var cmd = connection.CreateCommand(); cmd.CommandText = "replace into userdata (id, userId, data) values (@1, @2, @3)"; cmd.AddParam("@1", userDataId); cmd.AddParam("@2", userId); cmd.AddParam("@3", serialized); using (var tran = connection.BeginTransaction()) { try { cmd.Transaction = tran; await cmd.ExecuteNonQueryAsync(cancellationToken); tran.Commit(); } catch (OperationCanceledException) { tran.Rollback(); } catch (Exception e) { Logger.ErrorException("Failed to commit transaction.", e); tran.Rollback(); } } } /// /// Gets the user data. /// /// The user id. /// The user data id. /// Task{UserItemData}. /// /// userId /// or /// userDataId /// public async Task GetUserData(Guid userId, Guid userDataId) { if (userId == Guid.Empty) { throw new ArgumentNullException("userId"); } if (userDataId == Guid.Empty) { throw new ArgumentNullException("userDataId"); } var cmd = connection.CreateCommand(); cmd.CommandText = "select data from userdata where id = @id and userId=@userId"; 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)) { if (reader.Read()) { using (var stream = GetStream(reader, 0)) { return _protobufSerializer.DeserializeFromStream(stream); } } } return null; } } }