diff --git a/src/FirebirdSql.EntityFrameworkCore.Firebird/FirebirdSql.EntityFrameworkCore.Firebird.csproj b/src/FirebirdSql.EntityFrameworkCore.Firebird/FirebirdSql.EntityFrameworkCore.Firebird.csproj index 4baf2bd0c..eaf71ce5e 100644 --- a/src/FirebirdSql.EntityFrameworkCore.Firebird/FirebirdSql.EntityFrameworkCore.Firebird.csproj +++ b/src/FirebirdSql.EntityFrameworkCore.Firebird/FirebirdSql.EntityFrameworkCore.Firebird.csproj @@ -31,11 +31,13 @@ + + - + - + diff --git a/src/FirebirdSql.EntityFrameworkCore.Firebird/Infrastructure/FbDbContextOptionsBuilder.cs b/src/FirebirdSql.EntityFrameworkCore.Firebird/Infrastructure/FbDbContextOptionsBuilder.cs index 04e09712b..639560e35 100644 --- a/src/FirebirdSql.EntityFrameworkCore.Firebird/Infrastructure/FbDbContextOptionsBuilder.cs +++ b/src/FirebirdSql.EntityFrameworkCore.Firebird/Infrastructure/FbDbContextOptionsBuilder.cs @@ -32,4 +32,10 @@ public virtual FbDbContextOptionsBuilder WithExplicitParameterTypes(bool explici public virtual FbDbContextOptionsBuilder WithExplicitStringLiteralTypes(bool explicitStringLiteralTypes = true) => WithOption(e => e.WithExplicitStringLiteralTypes(explicitStringLiteralTypes)); + + public virtual FbDbContextOptionsBuilder WithUseCaseInsensitive(bool useCaseInsensitive = false) + => WithOption(e => e.WithUseCaseInsensitive(useCaseInsensitive)); + + public virtual FbDbContextOptionsBuilder WithIsUnicode(bool isUnicode = false) + => WithOption(e => e.WithIsUnicode(isUnicode)); } diff --git a/src/FirebirdSql.EntityFrameworkCore.Firebird/Infrastructure/Internal/FbOptionsExtension.cs b/src/FirebirdSql.EntityFrameworkCore.Firebird/Infrastructure/Internal/FbOptionsExtension.cs index e03e20674..b0ea3e6c9 100644 --- a/src/FirebirdSql.EntityFrameworkCore.Firebird/Infrastructure/Internal/FbOptionsExtension.cs +++ b/src/FirebirdSql.EntityFrameworkCore.Firebird/Infrastructure/Internal/FbOptionsExtension.cs @@ -28,7 +28,8 @@ public class FbOptionsExtension : RelationalOptionsExtension DbContextOptionsExtensionInfo _info; bool? _explicitParameterTypes; bool? _explicitStringLiteralTypes; - + bool? _useCaseInsensitive; + bool? _isUnicode; public FbOptionsExtension() { } @@ -37,6 +38,8 @@ public FbOptionsExtension(FbOptionsExtension copyFrom) { _explicitParameterTypes = copyFrom._explicitParameterTypes; _explicitStringLiteralTypes = copyFrom._explicitStringLiteralTypes; + _useCaseInsensitive = copyFrom._useCaseInsensitive; + _isUnicode = copyFrom._isUnicode; } protected override RelationalOptionsExtension Clone() @@ -48,6 +51,8 @@ public override void ApplyServices(IServiceCollection services) public override DbContextOptionsExtensionInfo Info => _info ??= new ExtensionInfo(this); public virtual bool? ExplicitParameterTypes => _explicitParameterTypes; public virtual bool? ExplicitStringLiteralTypes => _explicitStringLiteralTypes; + public virtual bool? UseCaseInsensitive => _useCaseInsensitive; + public virtual bool? IsUnicode => _isUnicode; public virtual FbOptionsExtension WithExplicitParameterTypes(bool explicitParameterTypes) { @@ -63,6 +68,20 @@ public virtual FbOptionsExtension WithExplicitStringLiteralTypes(bool explicitSt return clone; } + public virtual FbOptionsExtension WithUseCaseInsensitive(bool useCaseInsensitive) + { + var clone = (FbOptionsExtension)Clone(); + clone._useCaseInsensitive = useCaseInsensitive; + return clone; + } + + public virtual FbOptionsExtension WithIsUnicode(bool isUnicode) + { + var clone = (FbOptionsExtension)Clone(); + clone._isUnicode = isUnicode; + return clone; + } + sealed class ExtensionInfo : RelationalExtensionInfo { int? _serviceProviderHash; @@ -78,7 +97,6 @@ public override int GetServiceProviderHashCode() return _serviceProviderHash ??= HashCode.Combine(base.GetServiceProviderHashCode(), Extension._explicitParameterTypes, Extension._explicitStringLiteralTypes); } - public override void PopulateDebugInfo(IDictionary debugInfo) - { } + public override void PopulateDebugInfo(IDictionary debugInfo) => debugInfo["FirebirdSQL"] = "1.0"; } } diff --git a/src/FirebirdSql.EntityFrameworkCore.Firebird/Infrastructure/Internal/IFbOptions.cs b/src/FirebirdSql.EntityFrameworkCore.Firebird/Infrastructure/Internal/IFbOptions.cs index 7cd1447ea..62fddcad0 100644 --- a/src/FirebirdSql.EntityFrameworkCore.Firebird/Infrastructure/Internal/IFbOptions.cs +++ b/src/FirebirdSql.EntityFrameworkCore.Firebird/Infrastructure/Internal/IFbOptions.cs @@ -23,4 +23,6 @@ public interface IFbOptions : ISingletonOptions { bool ExplicitParameterTypes { get; } bool ExplicitStringLiteralTypes { get; } + bool UseCaseInsensitive { get; } + bool IsUnicode { get; } } diff --git a/src/FirebirdSql.EntityFrameworkCore.Firebird/Internal/FbOptions.cs b/src/FirebirdSql.EntityFrameworkCore.Firebird/Internal/FbOptions.cs index f87591877..926ed8b85 100644 --- a/src/FirebirdSql.EntityFrameworkCore.Firebird/Internal/FbOptions.cs +++ b/src/FirebirdSql.EntityFrameworkCore.Firebird/Internal/FbOptions.cs @@ -31,6 +31,8 @@ public virtual void Initialize(IDbContextOptions options) ExplicitParameterTypes = fbOptions.ExplicitParameterTypes ?? true; ExplicitStringLiteralTypes = fbOptions.ExplicitStringLiteralTypes ?? true; + UseCaseInsensitive = fbOptions.UseCaseInsensitive ?? true; + IsUnicode = fbOptions.IsUnicode ?? true; } public virtual void Validate(IDbContextOptions options) @@ -45,8 +47,18 @@ public virtual void Validate(IDbContextOptions options) { throw new InvalidOperationException($"A call was made to '{nameof(FbDbContextOptionsBuilder.WithExplicitStringLiteralTypes)}' that changed an option that must be constant within a service provider, but Entity Framework is not building its own internal service provider. Either allow EF to build the service provider by removing the call to '{nameof(DbContextOptionsBuilder.UseInternalServiceProvider)}', or ensure that the configuration for '{nameof(FbDbContextOptionsBuilder.WithExplicitStringLiteralTypes)}' does not change for all uses of a given service provider passed to '{nameof(DbContextOptionsBuilder.UseInternalServiceProvider)}'."); } + if (UseCaseInsensitive != (fbOptions.UseCaseInsensitive ?? true)) + { + throw new InvalidOperationException($"A call was made to '{nameof(FbDbContextOptionsBuilder.WithUseCaseInsensitive)}' that changed an option that must be constant within a service provider, but Entity Framework is not building its own internal service provider. Either allow EF to build the service provider by removing the call to '{nameof(DbContextOptionsBuilder.UseInternalServiceProvider)}', or ensure that the configuration for '{nameof(FbDbContextOptionsBuilder.WithExplicitStringLiteralTypes)}' does not change for all uses of a given service provider passed to '{nameof(DbContextOptionsBuilder.UseInternalServiceProvider)}'."); + } + if (IsUnicode != (fbOptions.IsUnicode ?? true)) + { + throw new InvalidOperationException($"A call was made to '{nameof(FbDbContextOptionsBuilder.WithIsUnicode)}' that changed an option that must be constant within a service provider, but Entity Framework is not building its own internal service provider. Either allow EF to build the service provider by removing the call to '{nameof(DbContextOptionsBuilder.UseInternalServiceProvider)}', or ensure that the configuration for '{nameof(FbDbContextOptionsBuilder.WithExplicitStringLiteralTypes)}' does not change for all uses of a given service provider passed to '{nameof(DbContextOptionsBuilder.UseInternalServiceProvider)}'."); + } } public virtual bool ExplicitParameterTypes { get; private set; } public virtual bool ExplicitStringLiteralTypes { get; private set; } + public virtual bool UseCaseInsensitive { get; private set; } + public virtual bool IsUnicode { get; private set; } } diff --git a/src/FirebirdSql.EntityFrameworkCore.Firebird/Query/Internal/FbQuerySqlGenerator.cs b/src/FirebirdSql.EntityFrameworkCore.Firebird/Query/Internal/FbQuerySqlGenerator.cs index 2e899cea6..19ea8ef4f 100644 --- a/src/FirebirdSql.EntityFrameworkCore.Firebird/Query/Internal/FbQuerySqlGenerator.cs +++ b/src/FirebirdSql.EntityFrameworkCore.Firebird/Query/Internal/FbQuerySqlGenerator.cs @@ -39,6 +39,32 @@ public FbQuerySqlGenerator(QuerySqlGeneratorDependencies dependencies, IFbOption _fbOptions = fbOptions; } + protected override Expression VisitBinary(BinaryExpression binaryExpression) + { + // Garante parênteses em volta de operações lógicas para manter a precedência do Front-end + Sql.Append("("); + Visit(binaryExpression.Left); + + var op = binaryExpression.NodeType switch + { + ExpressionType.AndAlso => " AND ", + ExpressionType.OrElse => " OR ", + ExpressionType.Equal => " = ", + ExpressionType.NotEqual => " <> ", + ExpressionType.LessThan => " < ", + ExpressionType.GreaterThan => " > ", + ExpressionType.LessThanOrEqual => " <= ", + ExpressionType.GreaterThanOrEqual => " >= ", + _ => base.VisitBinary(binaryExpression) == null ? "" : "" // Fallback padrão + }; + + Sql.Append(op); + Visit(binaryExpression.Right); + Sql.Append(")"); + + return binaryExpression; + } + protected override Expression VisitSqlUnary(SqlUnaryExpression sqlUnaryExpression) { if (sqlUnaryExpression.OperatorType == ExpressionType.Not && sqlUnaryExpression.TypeMapping.ClrType != typeof(bool)) @@ -143,22 +169,60 @@ protected override Expression VisitSqlBinary(SqlBinaryExpression sqlBinaryExpres Sql.Append(")"); return sqlBinaryExpression; } + //else if (sqlBinaryExpression.OperatorType == ExpressionType.AndAlso) + //{ + // if (sqlBinaryExpression.Left.Type == typeof(bool)) + // { + // var left = Sql.Append("UPPER("); + // Visit(sqlBinaryExpression.Left); + // Sql.Append(")"); + + // Sql.Append(GetOperatorType(sqlBinaryExpression.OperatorType)); + + // Sql.Append("UPPER("); + // Visit(sqlBinaryExpression.Right); + // Sql.Append(")"); + + // return sqlBinaryExpression; + // } + // return base.VisitSqlBinary(sqlBinaryExpression); + //} else { return base.VisitSqlBinary(sqlBinaryExpression); } + } - void BooleanToIntegralAndVisit(SqlExpression expression) + protected override Expression VisitColumn(ColumnExpression columnExpression) + { + if (columnExpression.Type == typeof(string)) { - Sql.Append("IIF("); - Visit(expression); - Sql.Append(", 1, 0)"); + var isUpper = _fbOptions.UseCaseInsensitive; + + if (isUpper) + { + Sql.Append("UPPER("); + } + base.VisitColumn(columnExpression); + if (isUpper) + { + Sql.Append(")"); + } + return columnExpression; } + return base.VisitColumn(columnExpression); } protected override Expression VisitSqlParameter(SqlParameterExpression sqlParameterExpression) { + var isUpper = _fbOptions.UseCaseInsensitive && sqlParameterExpression.Type == typeof(string); + var shouldExplicitParameterTypes = _fbOptions.ExplicitParameterTypes; + if (isUpper) + { + Sql.Append("UPPER("); + } + if (shouldExplicitParameterTypes) { Sql.Append("CAST("); @@ -169,8 +233,10 @@ protected override Expression VisitSqlParameter(SqlParameterExpression sqlParame Sql.Append(" AS "); if (sqlParameterExpression.Type == typeof(string)) { - var isUnicode = FbTypeMappingSource.IsUnicode(sqlParameterExpression.TypeMapping); - Sql.Append(((IFbSqlGenerationHelper)Dependencies.SqlGenerationHelper).StringParameterQueryType(isUnicode)); + var isUnicode = FbTypeMappingSource.IsUnicode(sqlParameterExpression.TypeMapping, _fbOptions) && _fbOptions.IsUnicode; + var storeTypeNameBase = sqlParameterExpression.TypeMapping.StoreTypeNameBase; + var size = sqlParameterExpression.TypeMapping.Size ?? 0; + Sql.Append(((IFbSqlGenerationHelper)Dependencies.SqlGenerationHelper).StringParameterQueryType(isUnicode, storeTypeNameBase, size)); } else { @@ -178,6 +244,10 @@ protected override Expression VisitSqlParameter(SqlParameterExpression sqlParame } Sql.Append(")"); } + if (isUpper) + { + Sql.Append(")"); + } return sqlParameterExpression; } @@ -191,9 +261,12 @@ protected override Expression VisitSqlConstant(SqlConstantExpression sqlConstant base.VisitSqlConstant(sqlConstantExpression); if (shouldExplicitStringLiteralTypes) { - var isUnicode = FbTypeMappingSource.IsUnicode(sqlConstantExpression.TypeMapping); + var isUnicode = FbTypeMappingSource.IsUnicode(sqlConstantExpression.TypeMapping, _fbOptions) && _fbOptions.IsUnicode; + var storeTypeNameBase = sqlConstantExpression.TypeMapping.StoreTypeNameBase; + var size = sqlConstantExpression.TypeMapping.Size ?? 0; + Sql.Append(" AS "); - Sql.Append(((IFbSqlGenerationHelper)Dependencies.SqlGenerationHelper).StringLiteralQueryType(sqlConstantExpression.Value as string, isUnicode)); + Sql.Append(((IFbSqlGenerationHelper)Dependencies.SqlGenerationHelper).StringLiteralQueryType(sqlConstantExpression.Value as string, isUnicode, storeTypeNameBase, size)); Sql.Append(")"); } return sqlConstantExpression; @@ -383,6 +456,88 @@ protected override Expression VisitExtension(Expression extensionExpression) }; } + protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) + { + var methodName = methodCallExpression.Method.Name; + + // 1. CONTAINS (Já implementado com CONTAINING) + if (methodName == "Contains" && methodCallExpression.Method.DeclaringType == typeof(string)) + { + Visit(methodCallExpression.Object); + Sql.Append(" CONTAINING "); + Visit(methodCallExpression.Arguments[0]); + return methodCallExpression; + } + + // 2. LIKE -> LIKE '%valor%' + if (methodName == "Like" && methodCallExpression.Method.DeclaringType == typeof(string)) + { + // Para Like ser insensitive, forçamos UPPER em ambos os lados. + // SQL: UPPER(Campo) LIKE UPPER('%' || 'valor' || '%') + Sql.Append("UPPER("); + Visit(methodCallExpression.Object ?? methodCallExpression.Arguments[0]); + Sql.Append(") LIKE UPPER('%' || "); + Visit(methodCallExpression.Arguments.Last()); + Sql.Append(" || '%')"); + return methodCallExpression; + } + + // 3. STARTSWITH -> LIKE 'valor%' + if (methodName == "StartsWith" && methodCallExpression.Method.DeclaringType == typeof(string)) + { + // 'STARTING WITH' é mais performático que LIKE para inícios de string. + // SQL: UPPER(Campo) STARTING WITH UPPER('valor') + Sql.Append("UPPER("); + Visit(methodCallExpression.Object); + Sql.Append(") STARTING WITH UPPER("); + Visit(methodCallExpression.Arguments[0]); + Sql.Append(")"); + return methodCallExpression; + } + + // 4. ENDSWITH -> LIKE '%valor' + if (methodName == "EndsWith" && methodCallExpression.Method.DeclaringType == typeof(string)) + { + // SQL: UPPER(Campo) LIKE UPPER('%' || 'valor') + Sql.Append("UPPER("); + Visit(methodCallExpression.Object); + Sql.Append(") LIKE UPPER('%' || "); + Visit(methodCallExpression.Arguments[0]); + Sql.Append(")"); + return methodCallExpression; + } + + // 5. SelectIN (Assume que o primeiro argumento é a lista e o segundo o valor, ou vice-versa) + // Geralmente mapeado de algo como "lista.Contains(item)" + if (methodName == "SelectIN" || (methodName == "Contains" && methodCallExpression.Method.DeclaringType != typeof(string))) + { + // Estrutura: VALOR IN (item1, item2, ...) + var column = methodCallExpression.Arguments.Count > 1 ? methodCallExpression.Arguments[1] : methodCallExpression.Arguments[0]; + var list = methodCallExpression.Arguments.Count > 1 ? methodCallExpression.Arguments[0] : methodCallExpression.Object; + + Visit(column); + Sql.Append(" IN ("); + Visit(list); // O EF Core geralmente expande constantes de lista automaticamente + Sql.Append(")"); + return methodCallExpression; + } + + // 6. NotSelectIN + if (methodName == "NotSelectIN") + { + var column = methodCallExpression.Arguments[1]; + var list = methodCallExpression.Arguments[0]; + + Visit(column); + Sql.Append(" NOT IN ("); + Visit(list); + Sql.Append(")"); + return methodCallExpression; + } + + return base.VisitMethodCall(methodCallExpression); + } + protected virtual Expression VisitSpacedFunction(FbSpacedFunctionExpression spacedFunctionExpression) { Sql.Append(spacedFunctionExpression.Name); @@ -413,4 +568,27 @@ void GenerateList(IReadOnlyList items, Action generationAction, Action< generationAction(items[i]); } } + + void BooleanToIntegralAndVisit(SqlExpression expression) + { + Sql.Append("IIF("); + Visit(expression); + Sql.Append(", 1, 0)"); + } + + string GetOperatorType(ExpressionType nodeType) + { + return nodeType switch + { + ExpressionType.AndAlso => " AND ", + ExpressionType.OrElse => " OR ", + ExpressionType.Equal => " = ", + ExpressionType.NotEqual => " <> ", + ExpressionType.LessThan => " < ", + ExpressionType.GreaterThan => " > ", + ExpressionType.LessThanOrEqual => " <= ", + ExpressionType.GreaterThanOrEqual => " >= ", + _ => "" + }; + } } diff --git a/src/FirebirdSql.EntityFrameworkCore.Firebird/Storage/Internal/FbSqlGenerationHelper.cs b/src/FirebirdSql.EntityFrameworkCore.Firebird/Storage/Internal/FbSqlGenerationHelper.cs index 78bbff57d..ead4bb4d3 100644 --- a/src/FirebirdSql.EntityFrameworkCore.Firebird/Storage/Internal/FbSqlGenerationHelper.cs +++ b/src/FirebirdSql.EntityFrameworkCore.Firebird/Storage/Internal/FbSqlGenerationHelper.cs @@ -27,17 +27,38 @@ public FbSqlGenerationHelper(RelationalSqlGenerationHelperDependencies dependenc : base(dependencies) { } - public virtual string StringLiteralQueryType(string s, bool isUnicode = true) + public virtual string StringLiteralQueryType(string s, bool isUnicode = true, string storeTypeNameBase = "", int size = 0) { - var length = MinimumStringQueryTypeLength(s); + var maxSize = MinimumStringQueryTypeLength(s); + string typeName; + if (storeTypeNameBase.Equals("BLOB SUB_TYPE TEXT", StringComparison.OrdinalIgnoreCase)) + { + typeName = "VARCHAR"; + } + else + { + typeName = IsEmpty(storeTypeNameBase) ? "VARCHAR" : storeTypeNameBase; + } + var charset = isUnicode ? " CHARACTER SET UTF8" : string.Empty; - return $"VARCHAR({length}){charset}"; + return $"{typeName}({maxSize}){charset}"; } - public virtual string StringParameterQueryType(bool isUnicode) + public virtual string StringParameterQueryType(bool isUnicode, string storeTypeNameBase = "", int size = 0) { - var size = isUnicode ? FbTypeMappingSource.UnicodeVarcharMaxSize : FbTypeMappingSource.VarcharMaxSize; - return $"VARCHAR({size})"; + int maxSize; + string typeName; + if (storeTypeNameBase.Equals("BLOB SUB_TYPE TEXT", StringComparison.OrdinalIgnoreCase)) + { + maxSize = (isUnicode ? FbTypeMappingSource.UnicodeVarcharMaxSize : FbTypeMappingSource.VarcharMaxSize); + typeName = "VARCHAR"; + } + else + { + maxSize = size > 0 ? size : (isUnicode ? FbTypeMappingSource.UnicodeVarcharMaxSize : FbTypeMappingSource.VarcharMaxSize); + typeName = IsEmpty(storeTypeNameBase) ? "VARCHAR" : storeTypeNameBase; + } + return $"{typeName}({maxSize})"; } public virtual void GenerateBlockParameterName(StringBuilder builder, string name) @@ -47,7 +68,7 @@ public virtual void GenerateBlockParameterName(StringBuilder builder, string nam public virtual string AlternativeStatementTerminator => "~"; - static int MinimumStringQueryTypeLength(string s) + private int MinimumStringQueryTypeLength(string s) { var length = s?.Length ?? 0; if (length == 0) @@ -55,9 +76,8 @@ static int MinimumStringQueryTypeLength(string s) return length; } - static void EnsureStringLiteralQueryTypeLength(int length) + private bool IsEmpty(string storeTypeNameBase) { - if (length > FbTypeMappingSource.UnicodeVarcharMaxSize) - throw new ArgumentOutOfRangeException(nameof(length)); + return (storeTypeNameBase == null || string.IsNullOrEmpty(storeTypeNameBase) || string.IsNullOrWhiteSpace(storeTypeNameBase)); } } diff --git a/src/FirebirdSql.EntityFrameworkCore.Firebird/Storage/Internal/FbTypeMappingSource.cs b/src/FirebirdSql.EntityFrameworkCore.Firebird/Storage/Internal/FbTypeMappingSource.cs index 9e41a9efb..8c9f0070c 100644 --- a/src/FirebirdSql.EntityFrameworkCore.Firebird/Storage/Internal/FbTypeMappingSource.cs +++ b/src/FirebirdSql.EntityFrameworkCore.Firebird/Storage/Internal/FbTypeMappingSource.cs @@ -19,6 +19,7 @@ using System.Collections.Generic; using System.Data; using FirebirdSql.Data.FirebirdClient; +using FirebirdSql.EntityFrameworkCore.Firebird.Infrastructure.Internal; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Storage; @@ -38,9 +39,9 @@ public class FbTypeMappingSource : RelationalTypeMappingSource readonly IntTypeMapping _integer = new IntTypeMapping("INTEGER", DbType.Int32); readonly LongTypeMapping _bigint = new LongTypeMapping("BIGINT", DbType.Int64); - readonly FbStringTypeMapping _char = new FbStringTypeMapping("CHAR", DbType.StringFixedLength, FbDbType.Char); - readonly FbStringTypeMapping _varchar = new FbStringTypeMapping("VARCHAR", DbType.String, FbDbType.VarChar); - readonly FbStringTypeMapping _clob = new FbStringTypeMapping("BLOB SUB_TYPE TEXT", DbType.String, FbDbType.Text); + readonly FbStringTypeMapping _char; + readonly FbStringTypeMapping _varchar; + readonly FbStringTypeMapping _clob; readonly FbByteArrayTypeMapping _binary = new FbByteArrayTypeMapping(); @@ -60,10 +61,17 @@ public class FbTypeMappingSource : RelationalTypeMappingSource readonly Dictionary _storeTypeMappings; readonly Dictionary _clrTypeMappings; readonly HashSet _disallowedMappings; + readonly IFbOptions _fbOptions; - public FbTypeMappingSource(TypeMappingSourceDependencies dependencies, RelationalTypeMappingSourceDependencies relationalDependencies) + public FbTypeMappingSource(TypeMappingSourceDependencies dependencies, RelationalTypeMappingSourceDependencies relationalDependencies, IFbOptions fbOptions) : base(dependencies, relationalDependencies) { + _fbOptions = fbOptions; + _char = new FbStringTypeMapping("CHAR", DbType.StringFixedLength, FbDbType.Char, unicode: fbOptions.IsUnicode); + _varchar = new FbStringTypeMapping("VARCHAR", DbType.String, FbDbType.VarChar, unicode: fbOptions.IsUnicode); + _clob = new FbStringTypeMapping("BLOB SUB_TYPE TEXT", DbType.String, FbDbType.Text, unicode: fbOptions.IsUnicode); + + _storeTypeMappings = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "BOOLEAN", _boolean }, @@ -111,7 +119,13 @@ public FbTypeMappingSource(TypeMappingSourceDependencies dependencies, Relationa protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo) { - return FindRawMapping(mappingInfo)?.Clone(mappingInfo) ?? base.FindMapping(mappingInfo); + var mapping = FindRawMapping(mappingInfo); + if (mapping != null) + { + var nonUnicodeInfo = mappingInfo with { IsUnicode = _fbOptions.IsUnicode && mapping.IsUnicode == true && mappingInfo.IsUnicode == true }; + return mapping.Clone(nonUnicodeInfo); + } + return mapping?.Clone(mappingInfo) ?? base.FindMapping(mappingInfo); } protected override void ValidateMapping(CoreTypeMapping mapping, IProperty property) @@ -134,7 +148,11 @@ RelationalTypeMapping FindRawMapping(RelationalTypeMappingInfo mappingInfo) var clrType = mappingInfo.ClrType; var storeTypeName = mappingInfo.StoreTypeName; var storeTypeNameBase = mappingInfo.StoreTypeNameBase; - var isUnicode = IsUnicode(mappingInfo.IsUnicode); + var isUnicode = IsUnicode(mappingInfo.IsUnicode, _fbOptions); + if (isUnicode && !_fbOptions.IsUnicode) + { + isUnicode = false; + } if (storeTypeName != null) { @@ -199,7 +217,7 @@ RelationalTypeMapping FindRawMapping(RelationalTypeMappingInfo mappingInfo) return null; } - public static bool IsUnicode(RelationalTypeMapping mapping) => IsUnicode(mapping?.IsUnicode); - public static bool IsUnicode(RelationalTypeMappingInfo mappingInfo) => IsUnicode(mappingInfo.IsUnicode); - public static bool IsUnicode(bool? isUnicode) => isUnicode ?? true; + public static bool IsUnicode(RelationalTypeMapping mapping, IFbOptions fbOptions) => IsUnicode(mapping?.IsUnicode, fbOptions); + public static bool IsUnicode(RelationalTypeMappingInfo mappingInfo, IFbOptions fbOptions) => IsUnicode(mappingInfo.IsUnicode, fbOptions); + public static bool IsUnicode(bool? isUnicode, IFbOptions fbOptions) => isUnicode ?? fbOptions.IsUnicode; } diff --git a/src/FirebirdSql.EntityFrameworkCore.Firebird/Storage/Internal/IFbSqlGenerationHelper.cs b/src/FirebirdSql.EntityFrameworkCore.Firebird/Storage/Internal/IFbSqlGenerationHelper.cs index 56c4c1f57..bb6af11c5 100644 --- a/src/FirebirdSql.EntityFrameworkCore.Firebird/Storage/Internal/IFbSqlGenerationHelper.cs +++ b/src/FirebirdSql.EntityFrameworkCore.Firebird/Storage/Internal/IFbSqlGenerationHelper.cs @@ -22,8 +22,8 @@ namespace FirebirdSql.EntityFrameworkCore.Firebird.Storage.Internal; public interface IFbSqlGenerationHelper : ISqlGenerationHelper { - string StringLiteralQueryType(string s, bool isUnicode); - string StringParameterQueryType(bool isUnicode); + string StringLiteralQueryType(string s, bool isUnicode, string storeTypeNameBase = "", int size = 0); + string StringParameterQueryType(bool isUnicode, string storeTypeNameBase = "", int size = 0); void GenerateBlockParameterName(StringBuilder builder, string name); string AlternativeStatementTerminator { get; } } diff --git a/src/Versions.props b/src/Versions.props index e134dd2ec..d06757548 100644 --- a/src/Versions.props +++ b/src/Versions.props @@ -1,4 +1,4 @@ - + 11.0.0-alpha1 @@ -15,4 +15,7 @@ 10.3.2 6.5.1 + + 1.7.1 +