using System.Collections.Generic; using System.Text; using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Storage; using NpgsqlTypes; namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping { /// /// The type mapping for the PostgreSQL hstore type. /// /// /// See: https://www.postgresql.org/docs/current/static/hstore.html /// public class NpgsqlHstoreTypeMapping : NpgsqlTypeMapping { static readonly HstoreComparer ComparerInstance = new HstoreComparer(); /// /// Constructs an instance of the class. /// public NpgsqlHstoreTypeMapping() : base( new RelationalTypeMappingParameters( new CoreTypeMappingParameters(typeof(Dictionary), null, ComparerInstance), "hstore" ), NpgsqlDbType.Hstore) {} protected NpgsqlHstoreTypeMapping(RelationalTypeMappingParameters parameters) : base(parameters, NpgsqlDbType.Hstore) {} protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) => new NpgsqlHstoreTypeMapping(parameters); protected override string GenerateNonNullSqlLiteral(object value) { var sb = new StringBuilder("HSTORE '"); foreach (var kv in (Dictionary)value) { sb.Append('"'); sb.Append(kv.Key); // TODO: Escape sb.Append("\"=>"); if (kv.Value == null) sb.Append("NULL"); else { sb.Append('"'); sb.Append(kv.Value); // TODO: Escape sb.Append("\","); } } sb.Remove(sb.Length - 1, 1); sb.Append('\''); return sb.ToString(); } class HstoreComparer : ValueComparer> { public HstoreComparer() : base( (a, b) => Compare(a,b), o => o.GetHashCode(), o => o == null ? null : new Dictionary(o)) {} static bool Compare(Dictionary a, Dictionary b) { if (a == null) return b == null; if (b == null) return false; if (a.Count != b.Count) return false; foreach (var kv in a) if (!b.TryGetValue(kv.Key, out var bValue) || kv.Value != bValue) return false; return true; } } } }