using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; using System; using System.Data; using System.IO; using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Server.Implementations.Persistence { /// /// Class SQLiteDisplayPreferencesRepository /// public class SqliteDisplayPreferencesRepository : IDisplayPreferencesRepository { private IDbConnection _connection; private readonly ILogger _logger; /// /// Gets the name of the repository /// /// The name. public string Name { get { return "SQLite"; } } /// /// The _json serializer /// private readonly IJsonSerializer _jsonSerializer; /// /// The _app paths /// private readonly IApplicationPaths _appPaths; private readonly SemaphoreSlim _writeLock = new SemaphoreSlim(1, 1); /// /// Initializes a new instance of the class. /// /// The app paths. /// The json serializer. /// The log manager. /// /// jsonSerializer /// or /// appPaths /// public SqliteDisplayPreferencesRepository(IApplicationPaths appPaths, IJsonSerializer jsonSerializer, ILogManager logManager) { if (jsonSerializer == null) { throw new ArgumentNullException("jsonSerializer"); } if (appPaths == null) { throw new ArgumentNullException("appPaths"); } _jsonSerializer = jsonSerializer; _appPaths = appPaths; _logger = logManager.GetLogger(GetType().Name); } /// /// Opens the connection to the database /// /// Task. public async Task Initialize() { var dbFile = Path.Combine(_appPaths.DataPath, "displaypreferences.db"); _connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false); string[] queries = { "create table if not exists userdisplaypreferences (id GUID, userId GUID, client text, data BLOB)", "create unique index if not exists userdisplaypreferencesindex on userdisplaypreferences (id, userId, client)", //pragmas "pragma temp_store = memory", "pragma shrink_memory" }; _connection.RunQueries(queries, _logger); } /// /// Save the display preferences associated with an item in the repo /// /// The display preferences. /// The user id. /// The client. /// The cancellation token. /// Task. /// item public async Task SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client, CancellationToken cancellationToken) { if (displayPreferences == null) { throw new ArgumentNullException("displayPreferences"); } if (string.IsNullOrWhiteSpace(displayPreferences.Id)) { throw new ArgumentNullException("displayPreferences.Id"); } cancellationToken.ThrowIfCancellationRequested(); var serialized = _jsonSerializer.SerializeToBytes(displayPreferences); await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); IDbTransaction transaction = null; try { transaction = _connection.BeginTransaction(); using (var cmd = _connection.CreateCommand()) { cmd.CommandText = "replace into userdisplaypreferences (id, userid, client, data) values (@1, @2, @3, @4)"; cmd.Parameters.Add(cmd, "@1", DbType.Guid).Value = new Guid(displayPreferences.Id); cmd.Parameters.Add(cmd, "@2", DbType.Guid).Value = userId; cmd.Parameters.Add(cmd, "@3", DbType.String).Value = client; cmd.Parameters.Add(cmd, "@4", DbType.Binary).Value = serialized; cmd.Transaction = transaction; cmd.ExecuteNonQuery(); } transaction.Commit(); } catch (OperationCanceledException) { if (transaction != null) { transaction.Rollback(); } throw; } catch (Exception e) { _logger.ErrorException("Failed to save display preferences:", e); if (transaction != null) { transaction.Rollback(); } throw; } finally { if (transaction != null) { transaction.Dispose(); } _writeLock.Release(); } } /// /// Gets the display preferences. /// /// The display preferences id. /// The user id. /// The client. /// Task{DisplayPreferences}. /// item public DisplayPreferences GetDisplayPreferences(string displayPreferencesId, Guid userId, string client) { if (string.IsNullOrWhiteSpace(displayPreferencesId)) { throw new ArgumentNullException("displayPreferencesId"); } var cmd = _connection.CreateCommand(); cmd.CommandText = "select data from userdisplaypreferences where id = @id and userId=@userId and client=@client"; cmd.Parameters.Add(cmd, "@id", DbType.Guid).Value = displayPreferencesId.GetMD5(); cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId; cmd.Parameters.Add(cmd, "@client", DbType.String).Value = client; using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow)) { if (reader.Read()) { using (var stream = reader.GetMemoryStream(0)) { return _jsonSerializer.DeserializeFromStream(stream); } } } return null; } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private readonly object _disposeLock = new object(); /// /// Releases unmanaged and - optionally - managed resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool dispose) { if (dispose) { try { lock (_disposeLock) { if (_connection != null) { if (_connection.IsOpen()) { _connection.Close(); } _connection.Dispose(); _connection = null; } } } catch (Exception ex) { _logger.ErrorException("Error disposing database", ex); } } } } }