using System.Collections.Concurrent; using System.Data; using System.Reflection; using LinqToDB; using LinqToDB.Mapping; using LinqToDB.Metadata; using Nop.Core; namespace Nop.Data.Mapping; /// /// LINQ To DB metadata reader for schema created by FluentMigrator /// public partial class FluentMigratorMetadataReader : IMetadataReader { #region Utilities private static DataType DbTypeToDataType(DbType dbType) { return dbType switch { DbType.AnsiString => DataType.VarChar, DbType.AnsiStringFixedLength => DataType.VarChar, DbType.Binary => DataType.Binary, DbType.Boolean => DataType.Boolean, DbType.Byte => DataType.Byte, DbType.Currency => DataType.Money, DbType.Date => DataType.Date, DbType.DateTime => DataType.DateTime, DbType.DateTime2 => DataType.DateTime2, DbType.DateTimeOffset => DataType.DateTimeOffset, DbType.Decimal => DataType.Decimal, DbType.Double => DataType.Double, DbType.Guid => DataType.Guid, DbType.Int16 => DataType.Int16, DbType.Int32 => DataType.Int32, DbType.Int64 => DataType.Int64, DbType.Object => DataType.Undefined, DbType.SByte => DataType.SByte, DbType.Single => DataType.Single, DbType.String => DataType.NVarChar, DbType.StringFixedLength => DataType.NVarChar, DbType.Time => DataType.Time, DbType.UInt16 => DataType.UInt16, DbType.UInt32 => DataType.UInt32, DbType.UInt64 => DataType.UInt64, DbType.VarNumeric => DataType.VarNumeric, DbType.Xml => DataType.Xml, _ => DataType.Undefined }; } #endregion #region Methods /// /// Gets all mapping attributes on specified type. /// /// Attributes owner type. /// Array of mapping attributes. public MappingAttribute[] GetAttributes(Type type) { if (!type.IsSubclassOf(typeof(BaseEntity))) return []; var attribute = Types.GetOrAdd((type, null), _ => { var entityDescriptor = NopMappingSchema.GetEntityDescriptor(type); if (entityDescriptor is null) return null; return new TableAttribute() { Schema = entityDescriptor.SchemaName, Name = entityDescriptor.EntityName }; }); return [attribute]; } /// /// Gets all mapping attributes on specified type member. /// /// Member type. Could be used by some metadata providers to identify actual member owner type. /// Type member for which mapping attributes should be returned. /// Array of attributes. public MappingAttribute[] GetAttributes(Type type, MemberInfo memberInfo) { if (!type.IsSubclassOf(typeof(BaseEntity))) return []; var entityDescriptor = NopMappingSchema.GetEntityDescriptor(type); if (entityDescriptor is null) return []; var attribute = Types.GetOrAdd((type, memberInfo), _ => { var entityField = entityDescriptor.Fields.SingleOrDefault(cd => cd.Name.Equals(NameCompatibilityManager.GetColumnName(type, memberInfo.Name), StringComparison.OrdinalIgnoreCase)); if (entityField is null) return null; if (!(memberInfo as PropertyInfo)?.CanWrite ?? false) return null; return new ColumnAttribute { Name = entityField.Name, IsPrimaryKey = entityField.IsPrimaryKey, IsColumn = true, CanBeNull = entityField.IsNullable ?? false, Length = entityField.Size ?? 0, Precision = entityField.Precision ?? 0, IsIdentity = entityField.IsIdentity, DataType = DbTypeToDataType(entityField.Type) }; }); var result = new List(); if (attribute is ColumnAttribute column) { result.Add(column); //define sequence name for supported db engines to retrieve identity values (PostgreSQL) if (column.IsIdentity) result.Add(new SequenceNameAttribute($"{entityDescriptor.EntityName}_{column.Name}_seq")); } return result.ToArray(); } /// /// Gets the dynamic columns defined on given type. /// /// The type. /// All dynamic columns defined on given type. public MemberInfo[] GetDynamicColumns(Type type) { return []; } /// /// Should return a unique ID for cache purposes. If the implemented Metadata reader returns instance-specific /// data you'll need to calculate a unique value based on content. Otherwise just use a static const /// e.g. $".{nameof(YourMetadataReader)}." /// /// The object ID as string public string GetObjectID() { return $".{nameof(FluentMigratorMetadataReader)}."; } #endregion #region Properties protected static ConcurrentDictionary<(Type, MemberInfo), MappingAttribute> Types { get; } = new ConcurrentDictionary<(Type, MemberInfo), MappingAttribute>(); #endregion }