I18n через jsonb-поля
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Pf2eModel.Model.Structure;
|
||||
using static LinqToDB.Common.Configuration;
|
||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
using System.Buffers;
|
||||
|
||||
#nullable disable
|
||||
|
||||
@@ -22,46 +26,76 @@ BEGIN
|
||||
|| ' CASCADE;'
|
||||
FROM pg_tables
|
||||
WHERE schemaname = 'public'
|
||||
AND tablename != '__EFMigrationsHistory'
|
||||
AND tablename != '__EFMigrationsHistory'
|
||||
);
|
||||
END
|
||||
$func$;
|
||||
""");
|
||||
migrationBuilder.Sql("""
|
||||
CREATE OR REPLACE FUNCTION f_drop_tables()
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql AS
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql AS
|
||||
$func$
|
||||
BEGIN
|
||||
EXECUTE
|
||||
(SELECT 'DROP TABLE '
|
||||
|| string_agg(format('%I.%I', schemaname, tablename), ', ')
|
||||
|| ' CASCADE;'
|
||||
FROM pg_tables
|
||||
WHERE schemaname = 'public'
|
||||
);
|
||||
EXECUTE
|
||||
(SELECT 'DROP TABLE '
|
||||
|| string_agg(format('%I.%I', schemaname, tablename), ', ')
|
||||
|| ' CASCADE;'
|
||||
FROM pg_tables
|
||||
WHERE schemaname = 'public'
|
||||
);
|
||||
END
|
||||
$func$;
|
||||
""");
|
||||
migrationBuilder.Sql("""
|
||||
CREATE OR REPLACE FUNCTION f_permission_tables(_username text)
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql AS
|
||||
$func$
|
||||
RETURNS void
|
||||
LANGUAGE plpgsql AS
|
||||
$func$
|
||||
DECLARE
|
||||
_tbl text;
|
||||
_sch text;
|
||||
_tbl text;
|
||||
_sch text;
|
||||
BEGIN
|
||||
FOR _sch, _tbl IN
|
||||
SELECT schemaname, tablename
|
||||
FROM pg_tables
|
||||
WHERE schemaname = 'public'
|
||||
LOOP
|
||||
EXECUTE format('ALTER TABLE %I.%I OWNER TO %I', _sch, _tbl, _username);
|
||||
END LOOP;
|
||||
FOR _sch, _tbl IN
|
||||
SELECT schemaname, tablename
|
||||
FROM pg_tables
|
||||
WHERE schemaname = 'public'
|
||||
LOOP
|
||||
EXECUTE format('ALTER TABLE %I.%I OWNER TO %I', _sch, _tbl, _username);
|
||||
END LOOP;
|
||||
END
|
||||
$func$;
|
||||
""");
|
||||
"""
|
||||
);
|
||||
|
||||
migrationBuilder.Sql("""
|
||||
CREATE OR REPLACE FUNCTION locale_search(data jsonb, search_value text)
|
||||
RETURNS boolean
|
||||
LANGUAGE plpgsql AS
|
||||
$func$
|
||||
BEGIN
|
||||
RETURN jsonb_path_exists(data, '$.** ? (@ == $val)', jsonb_build_object('val', search_value));
|
||||
END;
|
||||
$func$;
|
||||
"""
|
||||
);
|
||||
|
||||
migrationBuilder.Sql("""
|
||||
CREATE OR REPLACE FUNCTION locale_search_array(data jsonb, search_values text[])
|
||||
RETURNS boolean
|
||||
LANGUAGE plpgsql AS
|
||||
$func$
|
||||
BEGIN
|
||||
RETURN EXISTS (
|
||||
SELECT 1
|
||||
FROM unnest(search_values) AS val
|
||||
WHERE jsonb_path_exists(data, '$.** ? (@ == $val)', jsonb_build_object('val', val))
|
||||
);
|
||||
END;
|
||||
$func$;
|
||||
"""
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -70,5 +104,7 @@ $func$;
|
||||
migrationBuilder.Sql("DROP FUNCTION f_truncate_tables;");
|
||||
migrationBuilder.Sql("DROP FUNCTION f_drop_tables;");
|
||||
migrationBuilder.Sql("DROP FUNCTION f_permission_tables;");
|
||||
migrationBuilder.Sql("DROP FUNCTION locale_search;");
|
||||
migrationBuilder.Sql("DROP FUNCTION locale_search_array;");
|
||||
}
|
||||
}
|
||||
|
||||
+3
-43
@@ -13,7 +13,7 @@ using Pf2eModel.Model.Enum;
|
||||
namespace Pf2eModel.Migrations
|
||||
{
|
||||
[DbContext(typeof(Pf2eDbContext))]
|
||||
[Migration("20250421123520_1")]
|
||||
[Migration("20250424112427_1")]
|
||||
partial class _1
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@@ -25,7 +25,6 @@ namespace Pf2eModel.Migrations
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "action_category", new[] { "unknown", "archetype", "class-feature", "class-feat", "offensive", "defensive", "interaction", "familiar", "army_tactic", "army_war_action", "kingdom_activity", "kingdom_feat", "kingdom_feature" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "alias_type", new[] { "legacy", "english", "legacy_english", "hobby", "other" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "ancestry_vision", new[] { "normal", "low-light-vision", "dark-vision" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "attribute_values", new[] { "free", "str", "dex", "con", "int", "wis", "cha" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "class_progression_type", new[] { "entity", "ancestry_feat", "class_feat", "general_feat", "skill_feat", "skill_increase" });
|
||||
@@ -37,6 +36,7 @@ namespace Pf2eModel.Migrations
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "entity_type", new[] { "unknown", "trait", "action", "ancestry", "archetype", "background", "backpack", "campaign_feature", "class", "condition", "deity", "feat", "heritage", "spell", "language" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "feat_action_type", new[] { "passive", "action", "reaction", "free" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "feat_category", new[] { "uncategorized", "ancestry-feature", "class-feature", "calling", "class", "skill", "general", "ancestry", "bonus", "deityboon", "curse", "pfsboon", "campaign-feature" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "locale_type", new[] { "english", "legacy_english", "russian", "legacy", "hobby", "other" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "size", new[] { "tiny", "small", "medium", "large" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "source_category", new[] { "rulebook", "adventure-path", "lost-omens", "adventures", "comics", "blog-posts", "society" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "spell_area_type", new[] { "burst", "emanation", "cone", "line", "square", "cube", "cylinder" });
|
||||
@@ -142,8 +142,7 @@ namespace Pf2eModel.Migrations
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("name")
|
||||
.HasColumnOrder(2)
|
||||
.HasComment("Отображаемое имя");
|
||||
@@ -173,31 +172,6 @@ namespace Pf2eModel.Migrations
|
||||
b.ToTable("entities", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Pf2eModel.Model.Base.EntityAlias", b =>
|
||||
{
|
||||
b.Property<int>("EntityId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("entity_id")
|
||||
.HasColumnOrder(1);
|
||||
|
||||
b.Property<AliasType>("Type")
|
||||
.HasColumnType("alias_type")
|
||||
.HasColumnName("type")
|
||||
.HasColumnOrder(2);
|
||||
|
||||
b.Property<string>("Alias")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)")
|
||||
.HasColumnName("alias")
|
||||
.HasColumnOrder(3);
|
||||
|
||||
b.HasKey("EntityId", "Type")
|
||||
.HasName("pk_entity_aliases");
|
||||
|
||||
b.ToTable("entity_aliases", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Pf2eModel.Model.Base.Keywords.Keyword", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -1536,18 +1510,6 @@ namespace Pf2eModel.Migrations
|
||||
.HasConstraintName("fk_entity_trait_traits_traits_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Pf2eModel.Model.Base.EntityAlias", b =>
|
||||
{
|
||||
b.HasOne("Pf2eModel.Model.Base.Entity", "Entity")
|
||||
.WithMany("Aliases")
|
||||
.HasForeignKey("EntityId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_entity_aliases_entities_entity_id");
|
||||
|
||||
b.Navigation("Entity");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Pf2eModel.Model.Base.Keywords.KeywordAlias", b =>
|
||||
{
|
||||
b.HasOne("Pf2eModel.Model.Base.Keywords.Keyword", "Keyword")
|
||||
@@ -2056,8 +2018,6 @@ namespace Pf2eModel.Migrations
|
||||
|
||||
modelBuilder.Entity("Pf2eModel.Model.Base.Entity", b =>
|
||||
{
|
||||
b.Navigation("Aliases");
|
||||
|
||||
b.Navigation("Externals");
|
||||
|
||||
b.Navigation("Sources");
|
||||
+2
-24
@@ -15,7 +15,6 @@ namespace Pf2eModel.Migrations
|
||||
{
|
||||
migrationBuilder.AlterDatabase()
|
||||
.Annotation("Npgsql:Enum:action_category", "unknown,archetype,class-feature,class-feat,offensive,defensive,interaction,familiar,army_tactic,army_war_action,kingdom_activity,kingdom_feat,kingdom_feature")
|
||||
.Annotation("Npgsql:Enum:alias_type", "legacy,english,legacy_english,hobby,other")
|
||||
.Annotation("Npgsql:Enum:ancestry_vision", "normal,low-light-vision,dark-vision")
|
||||
.Annotation("Npgsql:Enum:attribute_values", "free,str,dex,con,int,wis,cha")
|
||||
.Annotation("Npgsql:Enum:class_progression_type", "entity,ancestry_feat,class_feat,general_feat,skill_feat,skill_increase")
|
||||
@@ -27,6 +26,7 @@ namespace Pf2eModel.Migrations
|
||||
.Annotation("Npgsql:Enum:entity_type", "unknown,trait,action,ancestry,archetype,background,backpack,campaign_feature,class,condition,deity,feat,heritage,spell,language")
|
||||
.Annotation("Npgsql:Enum:feat_action_type", "passive,action,reaction,free")
|
||||
.Annotation("Npgsql:Enum:feat_category", "uncategorized,ancestry-feature,class-feature,calling,class,skill,general,ancestry,bonus,deityboon,curse,pfsboon,campaign-feature")
|
||||
.Annotation("Npgsql:Enum:locale_type", "english,legacy_english,russian,legacy,hobby,other")
|
||||
.Annotation("Npgsql:Enum:size", "tiny,small,medium,large")
|
||||
.Annotation("Npgsql:Enum:source_category", "rulebook,adventure-path,lost-omens,adventures,comics,blog-posts,society")
|
||||
.Annotation("Npgsql:Enum:spell_area_type", "burst,emanation,cone,line,square,cube,cylinder")
|
||||
@@ -41,7 +41,7 @@ namespace Pf2eModel.Migrations
|
||||
{
|
||||
id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
name = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false, comment: "Отображаемое имя"),
|
||||
name = table.Column<string>(type: "jsonb", nullable: false, comment: "Отображаемое имя"),
|
||||
type = table.Column<EntityType>(type: "entity_type", nullable: false, comment: "Тип объекта"),
|
||||
slug = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false, comment: "Часть имени в URL"),
|
||||
img = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false, comment: "Изображение объекта"),
|
||||
@@ -250,25 +250,6 @@ namespace Pf2eModel.Migrations
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "entity_aliases",
|
||||
columns: table => new
|
||||
{
|
||||
entity_id = table.Column<int>(type: "integer", nullable: false),
|
||||
type = table.Column<AliasType>(type: "alias_type", nullable: false),
|
||||
alias = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_entity_aliases", x => new { x.entity_id, x.type });
|
||||
table.ForeignKey(
|
||||
name: "fk_entity_aliases_entities_entity_id",
|
||||
column: x => x.entity_id,
|
||||
principalTable: "entities",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "feats",
|
||||
columns: table => new
|
||||
@@ -1189,9 +1170,6 @@ namespace Pf2eModel.Migrations
|
||||
migrationBuilder.DropTable(
|
||||
name: "deity_weapons");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "entity_aliases");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "entity_externals");
|
||||
|
||||
@@ -22,7 +22,6 @@ namespace Pf2eModel.Migrations
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "action_category", new[] { "unknown", "archetype", "class-feature", "class-feat", "offensive", "defensive", "interaction", "familiar", "army_tactic", "army_war_action", "kingdom_activity", "kingdom_feat", "kingdom_feature" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "alias_type", new[] { "legacy", "english", "legacy_english", "hobby", "other" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "ancestry_vision", new[] { "normal", "low-light-vision", "dark-vision" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "attribute_values", new[] { "free", "str", "dex", "con", "int", "wis", "cha" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "class_progression_type", new[] { "entity", "ancestry_feat", "class_feat", "general_feat", "skill_feat", "skill_increase" });
|
||||
@@ -34,6 +33,7 @@ namespace Pf2eModel.Migrations
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "entity_type", new[] { "unknown", "trait", "action", "ancestry", "archetype", "background", "backpack", "campaign_feature", "class", "condition", "deity", "feat", "heritage", "spell", "language" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "feat_action_type", new[] { "passive", "action", "reaction", "free" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "feat_category", new[] { "uncategorized", "ancestry-feature", "class-feature", "calling", "class", "skill", "general", "ancestry", "bonus", "deityboon", "curse", "pfsboon", "campaign-feature" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "locale_type", new[] { "english", "legacy_english", "russian", "legacy", "hobby", "other" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "size", new[] { "tiny", "small", "medium", "large" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "source_category", new[] { "rulebook", "adventure-path", "lost-omens", "adventures", "comics", "blog-posts", "society" });
|
||||
NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "spell_area_type", new[] { "burst", "emanation", "cone", "line", "square", "cube", "cylinder" });
|
||||
@@ -139,8 +139,7 @@ namespace Pf2eModel.Migrations
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("name")
|
||||
.HasColumnOrder(2)
|
||||
.HasComment("Отображаемое имя");
|
||||
@@ -170,31 +169,6 @@ namespace Pf2eModel.Migrations
|
||||
b.ToTable("entities", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Pf2eModel.Model.Base.EntityAlias", b =>
|
||||
{
|
||||
b.Property<int>("EntityId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("entity_id")
|
||||
.HasColumnOrder(1);
|
||||
|
||||
b.Property<AliasType>("Type")
|
||||
.HasColumnType("alias_type")
|
||||
.HasColumnName("type")
|
||||
.HasColumnOrder(2);
|
||||
|
||||
b.Property<string>("Alias")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)")
|
||||
.HasColumnName("alias")
|
||||
.HasColumnOrder(3);
|
||||
|
||||
b.HasKey("EntityId", "Type")
|
||||
.HasName("pk_entity_aliases");
|
||||
|
||||
b.ToTable("entity_aliases", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Pf2eModel.Model.Base.Keywords.Keyword", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -1533,18 +1507,6 @@ namespace Pf2eModel.Migrations
|
||||
.HasConstraintName("fk_entity_trait_traits_traits_id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Pf2eModel.Model.Base.EntityAlias", b =>
|
||||
{
|
||||
b.HasOne("Pf2eModel.Model.Base.Entity", "Entity")
|
||||
.WithMany("Aliases")
|
||||
.HasForeignKey("EntityId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_entity_aliases_entities_entity_id");
|
||||
|
||||
b.Navigation("Entity");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Pf2eModel.Model.Base.Keywords.KeywordAlias", b =>
|
||||
{
|
||||
b.HasOne("Pf2eModel.Model.Base.Keywords.Keyword", "Keyword")
|
||||
@@ -2053,8 +2015,6 @@ namespace Pf2eModel.Migrations
|
||||
|
||||
modelBuilder.Entity("Pf2eModel.Model.Base.Entity", b =>
|
||||
{
|
||||
b.Navigation("Aliases");
|
||||
|
||||
b.Navigation("Externals");
|
||||
|
||||
b.Navigation("Sources");
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Pf2eModel.Model.Base.Keywords;
|
||||
using Pf2eModel.Model.Base.Source;
|
||||
using Pf2eModel.Model.Base.Targets;
|
||||
using Pf2eModel.Model.Common.I18n;
|
||||
using Pf2eModel.Model.Enum;
|
||||
using Pf2eModel.Model.Structure;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
@@ -16,9 +17,8 @@ public class Entity
|
||||
public int Id { get; set; }
|
||||
|
||||
[Column(Order = 2)]
|
||||
[MaxLength(Pf2eDbContext.NameLength)]
|
||||
[Comment("Отображаемое имя")]
|
||||
public string Name { get; set; } = null!;
|
||||
public LocaleString Name { get; set; } = new();
|
||||
|
||||
[Column(Order = 3)]
|
||||
[Comment("Тип объекта")]
|
||||
@@ -53,8 +53,6 @@ public class Entity
|
||||
|
||||
public virtual ICollection<Keyword> Keywords { get; set; } = [];
|
||||
|
||||
public virtual ICollection<EntityAlias> Aliases { get; set; } = [];
|
||||
|
||||
public virtual ICollection<EntitySource> Sources { get; set; } = [];
|
||||
|
||||
public virtual ICollection<EntityExternal> Externals { get; set; } = [];
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Newtonsoft.Json;
|
||||
using Pf2eModel.Model.Enum;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Pf2eModel.Model.Base;
|
||||
|
||||
[PrimaryKey(nameof(EntityId), nameof(Type))]
|
||||
public partial class EntityAlias
|
||||
{
|
||||
[Column(Order = 1)]
|
||||
public int EntityId { get; set; }
|
||||
|
||||
[Column(Order = 2)]
|
||||
public AliasType Type { get; set; } = AliasType.Other;
|
||||
|
||||
[Column(Order = 3)]
|
||||
[MaxLength(Pf2eDbContext.NameLength)]
|
||||
public string Alias { get; set; } = null!;
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual Entity Entity { get; set; } = null!;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Pf2eModel.Model.Base;
|
||||
using System.ComponentModel;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Pf2eModel.Model.Common.I18n;
|
||||
public static class LocaleExtension
|
||||
{
|
||||
public static string GetTypeString(this LocaleType localeType)
|
||||
{
|
||||
FieldInfo fi = localeType.GetType().GetField(localeType.ToString())!;
|
||||
|
||||
DescriptionAttribute[] attributes = (DescriptionAttribute[]) fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
|
||||
|
||||
return (attributes.Length != 0 ? attributes.FirstOrDefault()?.Description : null) ?? localeType.ToString();
|
||||
}
|
||||
|
||||
public static LocaleType GetTypeByString(this string localeType)
|
||||
{
|
||||
foreach (System.Reflection.FieldInfo field in typeof(LocaleType).GetFields())
|
||||
{
|
||||
if (Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) is DescriptionAttribute descriptionAttribute)
|
||||
{
|
||||
if (descriptionAttribute.Description == localeType)
|
||||
{
|
||||
return (LocaleType) field.GetValue(null)!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidEnumArgumentException();
|
||||
}
|
||||
/*
|
||||
public static IQueryable<T> WhereLocale(
|
||||
this IQueryable<T> query, Expression<Func<T, LocaleString>> expression, string searchTerm) where T : class
|
||||
{
|
||||
return query.Where(e => DbContext.LocaleSearch(e.Name.ToJson(), searchTerm));
|
||||
}
|
||||
|
||||
public static IQueryable<Entity> WhereLocale(
|
||||
this IQueryable<Entity> query, ICollection<string> searchTerms)
|
||||
{
|
||||
return query.Where(e => EF.Functions.LocaleSearchArray(e.Name.ToJson(), searchTerms));
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Pf2eModel.Model.Common.I18n;
|
||||
|
||||
public class LocaleItem(LocaleType type, string value)
|
||||
{
|
||||
public LocaleType Locale { get; set; } = type;
|
||||
|
||||
public string Value { get; set; } = value;
|
||||
|
||||
public LocaleItem(KeyValuePair<string, string> item) : this(LocaleExtension.GetTypeByString(item.Key), item.Value) { }
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Serialization.Json;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Pf2eModel.Model.Common.I18n;
|
||||
|
||||
public class LocaleString : ICollection<LocaleItem>
|
||||
{
|
||||
private readonly List<LocaleItem> _items = [];
|
||||
private void AssureDefault()
|
||||
{
|
||||
if (_items.Count == 0)
|
||||
{
|
||||
_items.Add(new LocaleItem(LocaleType.English, string.Empty));
|
||||
}
|
||||
}
|
||||
|
||||
public int Count => _items.Count;
|
||||
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public ICollection<LocaleType> Types => _items.Select(c => c.Locale).ToList();
|
||||
|
||||
public LocaleString()
|
||||
{
|
||||
AssureDefault();
|
||||
}
|
||||
|
||||
private LocaleType _E(string localeString) => LocaleExtension.GetTypeByString(localeString);
|
||||
|
||||
private LocaleType RealLocale(LocaleType locale) => Contains(locale) ? locale : LocaleType.English;
|
||||
private LocaleType RealLocale(string localeString) => RealLocale(_E(localeString));
|
||||
|
||||
public LocaleItem GetItem(LocaleType locale) => this.First(c => c.Locale == RealLocale(locale));
|
||||
public LocaleItem GetItem(string locale) => this.First(c => c.Locale == RealLocale(locale));
|
||||
|
||||
public string this[LocaleType locale]
|
||||
{
|
||||
get => GetItem(locale).Value;
|
||||
set
|
||||
{
|
||||
if (Types.Contains(locale))
|
||||
{
|
||||
GetItem(locale).Value = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_items.Add(new LocaleItem(locale, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
public string this[string localeString]
|
||||
{
|
||||
get => this[_E(localeString)];
|
||||
set => this[_E(localeString)] = value;
|
||||
}
|
||||
|
||||
public void Add(LocaleItem item)
|
||||
{
|
||||
this[item.Locale] = item.Value;
|
||||
}
|
||||
public void Add(LocaleType locale, string value)
|
||||
{
|
||||
this[locale] = value;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_items.Clear();
|
||||
AssureDefault();
|
||||
}
|
||||
|
||||
public bool Contains(LocaleItem item)
|
||||
{
|
||||
return _items.Contains(item);
|
||||
}
|
||||
public bool Contains(LocaleType locale)
|
||||
{
|
||||
return Types.Contains(locale);
|
||||
}
|
||||
public bool Contains(string value)
|
||||
{
|
||||
return this.Any(item => item.Value == value);
|
||||
}
|
||||
|
||||
public void CopyTo(LocaleItem[] array, int arrayIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool Remove(LocaleItem item)
|
||||
{
|
||||
bool result = _items.Remove(item);
|
||||
|
||||
if (Count == 0)
|
||||
{
|
||||
AssureDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public IEnumerator<LocaleItem> GetEnumerator()
|
||||
{
|
||||
return _items.GetEnumerator();
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this[LocaleType.English];
|
||||
}
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
JObject json = [];
|
||||
|
||||
foreach (LocaleItem item in _items)
|
||||
{
|
||||
json.Add(item.Locale.GetTypeString(), item.Value);
|
||||
}
|
||||
|
||||
return json.ToString();
|
||||
}
|
||||
public static LocaleString Deserialize(string input)
|
||||
{
|
||||
JObject json = JObject.Parse(input);
|
||||
LocaleString result = new();
|
||||
|
||||
foreach (var item in json)
|
||||
{
|
||||
result[item.Key] = item.ToString();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Pf2eModel.Model.Common.I18n;
|
||||
|
||||
public enum LocaleType
|
||||
{
|
||||
[Description("en")]
|
||||
English,
|
||||
|
||||
[Description("en.legacy")]
|
||||
LegacyEnglish,
|
||||
|
||||
[Description("ru")]
|
||||
Russian,
|
||||
|
||||
[Description("ru.legacy")]
|
||||
Legacy,
|
||||
|
||||
[Description("ru.hobby")]
|
||||
Hobby,
|
||||
|
||||
[Description("other")]
|
||||
Other,
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
using NpgsqlTypes;
|
||||
|
||||
namespace Pf2eModel.Model.Enum;
|
||||
|
||||
public enum AliasType
|
||||
{
|
||||
[PgName("legacy")]
|
||||
Legacy,
|
||||
|
||||
[PgName("english")]
|
||||
English,
|
||||
|
||||
[PgName("legacy_english")]
|
||||
LegacyEnglish,
|
||||
|
||||
[PgName("hobby")]
|
||||
Hobby,
|
||||
|
||||
[PgName("other")]
|
||||
Other,
|
||||
}
|
||||
@@ -16,7 +16,7 @@ public partial class Ancestry : IEntity
|
||||
public Entity Entity { get; set; } = null!;
|
||||
|
||||
[Column(Order = 2)]
|
||||
public Size[] Size { get; set; }
|
||||
public Size[] Size { get; set; } = [];
|
||||
|
||||
[Column(Order = 3)]
|
||||
public int Hp { get; set; }
|
||||
|
||||
@@ -6,6 +6,7 @@ using Pf2eModel.Model.Base.Keywords;
|
||||
using Pf2eModel.Model.Base.Source;
|
||||
using Pf2eModel.Model.Base.Targets;
|
||||
using Pf2eModel.Model.Common;
|
||||
using Pf2eModel.Model.Common.I18n;
|
||||
using Pf2eModel.Model.Enum;
|
||||
using Pf2eModel.Model.Structure;
|
||||
using Pf2eModel.Model.Structure.AncestryRelation;
|
||||
@@ -72,8 +73,6 @@ public partial class Pf2eDbContext : DbContext
|
||||
|
||||
public virtual DbSet<Entity> Entities { get; set; }
|
||||
|
||||
public virtual DbSet<EntityAlias> EntityAliases { get; set; }
|
||||
|
||||
public virtual DbSet<EntityExternal> EntityExternals { get; set; }
|
||||
|
||||
public virtual DbSet<EntitySource> EntitySources { get; set; }
|
||||
@@ -125,7 +124,7 @@ public partial class Pf2eDbContext : DbContext
|
||||
builder
|
||||
.MapEnum<EntityType>()
|
||||
.MapEnum<EntityRarity>()
|
||||
.MapEnum<AliasType>()
|
||||
.MapEnum<LocaleType>()
|
||||
.MapEnum<TraitType>()
|
||||
.MapEnum<ActionCategory>()
|
||||
.MapEnum<Size>()
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
|
||||
using Pf2eModel.Model;
|
||||
using Pf2eModel.Model.Base;
|
||||
using Pf2eModel.Model.Base.Keywords;
|
||||
using Pf2eModel.Model.Base.Source;
|
||||
using Pf2eModel.Model.Base.Targets;
|
||||
using Pf2eModel.Model.Common;
|
||||
using Pf2eModel.Model.Common.I18n;
|
||||
using Pf2eModel.Model.Enum;
|
||||
using Pf2eModel.Model.Structure;
|
||||
using Pf2eModel.Model.Structure.AncestryRelation;
|
||||
@@ -15,6 +17,8 @@ using Pf2eModel.Model.Structure.ClassRelation;
|
||||
using Pf2eModel.Model.Structure.DeityRelation;
|
||||
using Pf2eModel.Model.Structure.FeatRelation;
|
||||
using Pf2eModel.Model.Structure.SpellRelation;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Pf2eModel;
|
||||
|
||||
@@ -30,6 +34,32 @@ public partial class Pf2eDbContext
|
||||
DefineBase(modelBuilder);
|
||||
DefineCommon(modelBuilder);
|
||||
DefineStructure(modelBuilder);
|
||||
DefineMethods(modelBuilder);
|
||||
}
|
||||
|
||||
private void BindEntity<T>(EntityTypeBuilder<T> entity) where T : class, IEntity
|
||||
{
|
||||
entity.HasOne(entity => entity.Entity)
|
||||
.WithOne()
|
||||
.HasForeignKey<T>(entity => entity.Id)
|
||||
.IsRequired();
|
||||
}
|
||||
|
||||
private void BindLocaleString<T>(EntityTypeBuilder<T> entity, Expression<Func<T, LocaleString>> func) where T : class
|
||||
{
|
||||
/*
|
||||
entity.OwnsMany(func, d =>
|
||||
{
|
||||
d.ToJson();
|
||||
});
|
||||
*/
|
||||
|
||||
entity.Property(func)
|
||||
.HasConversion(
|
||||
v => v.Serialize(),
|
||||
v => LocaleString.Deserialize(v)
|
||||
)
|
||||
.HasColumnType("jsonb");
|
||||
}
|
||||
|
||||
private void DefineEnums(ModelBuilder modelBuilder)
|
||||
@@ -37,7 +67,7 @@ public partial class Pf2eDbContext
|
||||
modelBuilder
|
||||
.HasPostgresEnum<EntityType>()
|
||||
.HasPostgresEnum<EntityRarity>()
|
||||
.HasPostgresEnum<AliasType>()
|
||||
.HasPostgresEnum<LocaleType>()
|
||||
.HasPostgresEnum<TraitType>()
|
||||
.HasPostgresEnum<ActionCategory>()
|
||||
.HasPostgresEnum<Size>()
|
||||
@@ -62,15 +92,12 @@ public partial class Pf2eDbContext
|
||||
{
|
||||
modelBuilder.Entity<Entity>(entity =>
|
||||
{
|
||||
BindLocaleString(entity, e => e.Name);
|
||||
|
||||
entity.HasMany(e => e.Traits).WithMany(t => t.Entities);
|
||||
|
||||
entity.HasMany(e => e.Keywords).WithMany(t => t.Entities);
|
||||
|
||||
// EntityAlias
|
||||
entity.HasMany(e => e.Aliases).WithOne(e => e.Entity)
|
||||
.HasForeignKey(e => e.EntityId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
// EntitySource
|
||||
entity.HasMany(e => e.Sources).WithOne(e => e.Entity)
|
||||
.HasForeignKey(e => e.EntityId)
|
||||
@@ -81,7 +108,6 @@ public partial class Pf2eDbContext
|
||||
.HasForeignKey(e => e.EntityId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
modelBuilder.Entity<EntityAlias>(entity => { });
|
||||
|
||||
// Source
|
||||
modelBuilder.Entity<Source>(entity =>
|
||||
@@ -124,14 +150,6 @@ public partial class Pf2eDbContext
|
||||
modelBuilder.Entity<Training>(entity => { });
|
||||
}
|
||||
|
||||
private void BindEntity<T>(EntityTypeBuilder<T> entity) where T : class, IEntity
|
||||
{
|
||||
entity.HasOne(entity => entity.Entity)
|
||||
.WithOne()
|
||||
.HasForeignKey<T>(entity => entity.Id)
|
||||
.IsRequired();
|
||||
}
|
||||
|
||||
private void DefineStructure(ModelBuilder modelBuilder)
|
||||
{
|
||||
// Action
|
||||
@@ -341,4 +359,30 @@ public partial class Pf2eDbContext
|
||||
BindEntity(entity);
|
||||
});
|
||||
}
|
||||
|
||||
private void DefineMethods(ModelBuilder modelBuilder)
|
||||
{
|
||||
MethodInfo localeSearch = typeof(Pf2eDbContext).GetMethod(nameof(LocaleSearch), BindingFlags.Static | BindingFlags.Public, [typeof(LocaleString), typeof(string)])!;
|
||||
modelBuilder.HasDbFunction(localeSearch, builder =>
|
||||
{
|
||||
builder.HasName("locale_search");
|
||||
builder.HasParameter("data").HasStoreType("jsonb");
|
||||
builder.HasParameter("search_value");
|
||||
});
|
||||
|
||||
MethodInfo localeSearchArray = typeof(Pf2eDbContext).GetMethod(nameof(LocaleSearchArray), BindingFlags.Static | BindingFlags.Public, [typeof(LocaleString), typeof(IEnumerable<string>)])!;
|
||||
modelBuilder.HasDbFunction(localeSearchArray, builder =>
|
||||
{
|
||||
builder.HasName("locale_search_array");
|
||||
builder.HasParameter("data").HasStoreType("jsonb");
|
||||
builder.HasParameter("search_values");
|
||||
});
|
||||
}
|
||||
|
||||
public static bool LocaleSearch(LocaleString data, string search_value)
|
||||
=> throw new InvalidOperationException("Only for LINQ translation");
|
||||
|
||||
public static bool LocaleSearchArray(LocaleString data, IEnumerable<string> search_values)
|
||||
=> throw new InvalidOperationException("Only for LINQ translation");
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<!--<PrivateAssets>all</PrivateAssets>-->
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.4" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Npgsql" Version="9.0.3" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
|
||||
Reference in New Issue
Block a user