using System; using System.Globalization; using System.Linq.Expressions; using System.Reflection; using EFCore.NamingConventions.Internal; using Kyoo.Abstractions.Models; using Microsoft.EntityFrameworkCore; using Npgsql; namespace Kyoo.Postgresql { /// /// A postgresql implementation of . /// public class PostgresContext : 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; static PostgresContext() { NpgsqlConnection.GlobalTypeMapper.MapEnum(); NpgsqlConnection.GlobalTypeMapper.MapEnum(); NpgsqlConnection.GlobalTypeMapper.MapEnum(); } /// /// A basic constructor that set default values (query tracker behaviors, mapping enums...) /// public PostgresContext() { } /// /// Create a new using specific options /// /// The options to use. public PostgresContext(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 PostgresContext(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.UseNpgsql(_connection); else optionsBuilder.UseNpgsql(); if (_debugMode) optionsBuilder.EnableDetailedErrors().EnableSensitiveDataLogging(); } optionsBuilder.UseSnakeCaseNamingConvention(); base.OnConfiguring(optionsBuilder); } /// /// Set database parameters to support every types of Kyoo. /// /// The database's model builder. protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasPostgresEnum(); modelBuilder.HasPostgresEnum(); modelBuilder.HasPostgresEnum(); modelBuilder.Entity() .ToView("library_items") .HasKey(x => x.ID); modelBuilder.Entity() .Property(x => x.ExtraData) .HasColumnType("jsonb"); modelBuilder.Entity() .Property(x => x.Images) .HasColumnType("jsonb"); modelBuilder.Entity() .Property(x => x.Images) .HasColumnType("jsonb"); modelBuilder.Entity() .Property(x => x.Images) .HasColumnType("jsonb"); modelBuilder.Entity() .Property(x => x.Images) .HasColumnType("jsonb"); modelBuilder.Entity() .Property(x => x.Images) .HasColumnType("jsonb"); modelBuilder.Entity() .Property(x => x.Images) .HasColumnType("jsonb"); modelBuilder.Entity() .Property(x => x.Images) .HasColumnType("jsonb"); modelBuilder.Entity() .Property(x => x.Images) .HasColumnType("jsonb"); base.OnModelCreating(modelBuilder); } /// protected override string MetadataName() { SnakeCaseNameRewriter rewriter = new(CultureInfo.InvariantCulture); return rewriter.RewriteName(typeof(T).Name + nameof(MetadataID)); } /// protected override string LinkName() { SnakeCaseNameRewriter rewriter = new(CultureInfo.InvariantCulture); return rewriter.RewriteName("Link" + typeof(T).Name + typeof(T2).Name); } /// protected override string LinkNameFk() { SnakeCaseNameRewriter rewriter = new(CultureInfo.InvariantCulture); return rewriter.RewriteName(typeof(T).Name + "ID"); } /// protected override bool IsDuplicateException(Exception ex) { return ex.InnerException is PostgresException {SqlState: PostgresErrorCodes.UniqueViolation}; } /// public override Expression> Like(Expression> query, string format) { MethodInfo iLike = MethodOfUtils.MethodOf(EF.Functions.ILike); MethodCallExpression call = Expression.Call(iLike, Expression.Constant(EF.Functions), query.Body, Expression.Constant(format)); return Expression.Lambda>(call, query.Parameters); } } }