using MediaBrowser.Controller; 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.Persistence { /// /// Class SQLiteUserRepository /// public class SqliteUserRepository : BaseSqliteRepository, IUserRepository { private IDbConnection _connection; private readonly IServerApplicationPaths _appPaths; private readonly IJsonSerializer _jsonSerializer; public SqliteUserRepository(ILogManager logManager, IServerApplicationPaths appPaths, IJsonSerializer jsonSerializer) : base(logManager) { _appPaths = appPaths; _jsonSerializer = jsonSerializer; } /// /// Gets the name of the repository /// /// The name. public string Name { get { return "SQLite"; } } /// /// Opens the connection to the database /// /// Task. public async Task Initialize(IDbConnector dbConnector) { var dbFile = Path.Combine(_appPaths.DataPath, "users.db"); _connection = await dbConnector.Connect(dbFile).ConfigureAwait(false); string[] queries = { "create table if not exists users (guid GUID primary key, data BLOB)", "create index if not exists idx_users on users(guid)", "create table if not exists schema_version (table_name primary key, version)", //pragmas "pragma temp_store = memory", "pragma shrink_memory" }; _connection.RunQueries(queries, Logger); } /// /// Save a user in the repo /// /// The user. /// The cancellation token. /// Task. /// user public async Task SaveUser(User user, CancellationToken cancellationToken) { if (user == null) { throw new ArgumentNullException("user"); } cancellationToken.ThrowIfCancellationRequested(); var serialized = _jsonSerializer.SerializeToBytes(user); cancellationToken.ThrowIfCancellationRequested(); await WriteLock.WaitAsync(cancellationToken).ConfigureAwait(false); IDbTransaction transaction = null; try { transaction = _connection.BeginTransaction(); using (var cmd = _connection.CreateCommand()) { cmd.CommandText = "replace into users (guid, data) values (@1, @2)"; cmd.Parameters.Add(cmd, "@1", DbType.Guid).Value = user.Id; cmd.Parameters.Add(cmd, "@2", 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 user:", e); if (transaction != null) { transaction.Rollback(); } throw; } finally { if (transaction != null) { transaction.Dispose(); } WriteLock.Release(); } } /// /// Retrieve all users from the database /// /// IEnumerable{User}. public IEnumerable RetrieveAllUsers() { using (var cmd = _connection.CreateCommand()) { cmd.CommandText = "select guid,data from users"; using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) { while (reader.Read()) { var id = reader.GetGuid(0); using (var stream = reader.GetMemoryStream(1)) { var user = _jsonSerializer.DeserializeFromStream(stream); user.Id = id; yield return user; } } } } } /// /// Deletes the user. /// /// The user. /// The cancellation token. /// Task. /// user public async Task DeleteUser(User user, CancellationToken cancellationToken) { if (user == null) { throw new ArgumentNullException("user"); } cancellationToken.ThrowIfCancellationRequested(); await WriteLock.WaitAsync(cancellationToken).ConfigureAwait(false); IDbTransaction transaction = null; try { transaction = _connection.BeginTransaction(); using (var cmd = _connection.CreateCommand()) { cmd.CommandText = "delete from users where guid=@guid"; cmd.Parameters.Add(cmd, "@guid", DbType.Guid).Value = user.Id; cmd.Transaction = transaction; cmd.ExecuteNonQuery(); } transaction.Commit(); } catch (OperationCanceledException) { if (transaction != null) { transaction.Rollback(); } throw; } catch (Exception e) { Logger.ErrorException("Failed to delete user:", e); if (transaction != null) { transaction.Rollback(); } throw; } finally { if (transaction != null) { transaction.Dispose(); } WriteLock.Release(); } } protected override void CloseConnection() { if (_connection != null) { if (_connection.IsOpen()) { _connection.Close(); } _connection.Dispose(); _connection = null; } } } }