using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; using Kyoo.Models; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Newtonsoft.Json; namespace Kyoo.SqLite { /// /// A sqlite implementation of . /// public class SqLiteContext : DatabaseContext { /// /// The connection string to use. /// private readonly string _connection; /// /// Is this instance in debug mode? /// private readonly bool _debugMode; /// /// Should the configure step be skipped? This is used when the database is created via DbContextOptions. /// private readonly bool _skipConfigure; /// /// A basic constructor that set default values (query tracker behaviors, mapping enums...) /// public SqLiteContext() { } /// /// Create a new using specific options /// /// The options to use. public SqLiteContext(DbContextOptions options) : base(options) { _skipConfigure = true; } /// /// A basic constructor that set default values (query tracker behaviors, mapping enums...) /// /// The connection string to use /// Is this instance in debug mode? public SqLiteContext(string connection, bool debugMode) { _connection = connection; _debugMode = debugMode; } /// /// Set connection information for this database context /// /// An option builder to fill. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { if (!_skipConfigure) { if (_connection != null) optionsBuilder.UseSqlite(_connection); else optionsBuilder.UseSqlite(); if (_debugMode) optionsBuilder.EnableDetailedErrors().EnableSensitiveDataLogging(); } base.OnConfiguring(optionsBuilder); } /// /// Set database parameters to support every types of Kyoo. /// /// The database's model builder. protected override void OnModelCreating(ModelBuilder modelBuilder) { ValueConverter arrayConvertor = new( x => string.Join(";", x), x => x.Split(';', StringSplitOptions.None)); modelBuilder.Entity() .Property(x => x.Paths) .HasConversion(arrayConvertor); modelBuilder.Entity() .Property(x => x.Aliases) .HasConversion(arrayConvertor); modelBuilder.Entity() .Property(x => x.Permissions) .HasConversion(arrayConvertor); modelBuilder.Entity() .Property(x => x.Status) .HasConversion(); modelBuilder.Entity() .Property(x => x.Type) .HasConversion(); ValueConverter, string> jsonConvertor = new( x => JsonConvert.SerializeObject(x), x => JsonConvert.DeserializeObject>(x)); modelBuilder.Entity() .Property(x => x.ExtraData) .HasConversion(jsonConvertor); modelBuilder.Entity() .ToView("LibraryItems") .HasKey(x => x.ID); base.OnModelCreating(modelBuilder); } /// protected override bool IsDuplicateException(Exception ex) { return ex.InnerException is SqliteException { SqliteExtendedErrorCode: 2067 /*SQLITE_CONSTRAINT_UNIQUE*/} or SqliteException { SqliteExtendedErrorCode: 1555 /*SQLITE_CONSTRAINT_PRIMARYKEY*/}; } /// public override Expression> Like(Expression> query, string format) { MethodInfo iLike = MethodOfUtils.MethodOf(EF.Functions.Like); MethodCallExpression call = Expression.Call(iLike, Expression.Constant(EF.Functions), query.Body, Expression.Constant(format)); return Expression.Lambda>(call, query.Parameters); } } }