Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,13 @@ public class SqliteLoggingDefinitions : RelationalLoggingDefinitions
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public EventDefinitionBase LogFoundUniqueConstraint;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public EventDefinitionBase LogUnexpectedConnectionType;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.EntityFrameworkCore.Diagnostics;
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace Microsoft.EntityFrameworkCore.Sqlite.Diagnostics.Internal
{
/// <summary>
/// The <see cref="DiagnosticSource" /> event payload for
/// <see cref="SqliteEventId.UnexpectedConnectionTypeWarning"/>.
/// </summary>
public class UnexpectedConnectionTypeEventData : EventData
{
/// <summary>
/// Constructs the event payload.
/// </summary>
/// <param name="eventDefinition"> The event definition. </param>
/// <param name="messageGenerator"> A delegate that generates a log message for this event. </param>
/// <param name="connectionType"> The connection type. </param>
public UnexpectedConnectionTypeEventData(
[NotNull] EventDefinitionBase eventDefinition,
[NotNull] Func<EventDefinitionBase, EventData, string> messageGenerator,
[NotNull] Type connectionType)
: base(eventDefinition, messageGenerator)
{
ConnectionType = connectionType;
}

/// <summary>
/// The connection type.
/// </summary>
public virtual Type ConnectionType { get; }
}
}
21 changes: 21 additions & 0 deletions src/EFCore.Sqlite.Core/Diagnostics/SqliteEventId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Diagnostics;
using Microsoft.EntityFrameworkCore.Sqlite.Diagnostics.Internal;
using Microsoft.Extensions.Logging;

namespace Microsoft.EntityFrameworkCore.Diagnostics
Expand All @@ -27,6 +28,9 @@ private enum Id
SchemaConfiguredWarning = CoreEventId.ProviderBaseId,
SequenceConfiguredWarning,

// Infrastructure events
UnexpectedConnectionTypeWarning = CoreEventId.ProviderBaseId + 100,

// Scaffolding events
ColumnFound = CoreEventId.ProviderDesignBaseId,
ForeignKeyFound,
Expand Down Expand Up @@ -69,6 +73,23 @@ private enum Id
/// </summary>
public static readonly EventId SequenceConfiguredWarning = MakeValidationId(Id.SequenceConfiguredWarning);

private static readonly string _infraPrefix = DbLoggerCategory.Infrastructure.Name + ".";
private static EventId MakeInfraId(Id id) => new EventId((int)id, _infraPrefix + id);

/// <summary>
/// <para>
/// A connection of an unexpected type is being used.
/// </para>
/// <para>
/// This event is in the <see cref="DbLoggerCategory.Infrastructure" /> category.
/// </para>
/// <para>
/// This event uses the <see cref="UnexpectedConnectionTypeEventData" />
/// payload when used with a <see cref="DiagnosticSource" />.
/// </para>
/// </summary>
public static readonly EventId UnexpectedConnectionTypeWarning = MakeInfraId(Id.UnexpectedConnectionTypeWarning);

private static readonly string _scaffoldingPrefix = DbLoggerCategory.Scaffolding.Name + ".";
private static EventId MakeScaffoldingId(Id id) => new EventId((int)id, _scaffoldingPrefix + id);

Expand Down
39 changes: 39 additions & 0 deletions src/EFCore.Sqlite.Core/Internal/SqliteLoggerExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Sqlite.Diagnostics.Internal;

namespace Microsoft.EntityFrameworkCore.Sqlite.Internal
{
Expand Down Expand Up @@ -301,5 +304,41 @@ public static void UniqueConstraintFound(

// No DiagnosticsSource events because these are purely design-time messages
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public static void UnexpectedConnectionTypeWarning(
[NotNull] this IDiagnosticsLogger<DbLoggerCategory.Infrastructure> diagnostics,
[NotNull] Type connectionType)
{
var definition = SqliteResources.LogUnexpectedConnectionType(diagnostics);

if (diagnostics.ShouldLog(definition))
{
definition.Log(diagnostics, connectionType.DisplayName());
}

if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
{
var eventData = new UnexpectedConnectionTypeEventData(
definition,
UnexpectedConnectionTypeWarning,
connectionType);

diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
}
}

private static string UnexpectedConnectionTypeWarning(EventDefinitionBase definition, EventData payload)
{
var d = (EventDefinition<string>)definition;
var p = (UnexpectedConnectionTypeEventData)payload;

return d.GenerateMessage(p.ConnectionType.DisplayName());
}
}
}
24 changes: 24 additions & 0 deletions src/EFCore.Sqlite.Core/Properties/SqliteStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions src/EFCore.Sqlite.Core/Properties/SqliteStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,8 @@
<data name="OrderByNotSupported" xml:space="preserve">
<value>SQLite cannot order by expressions of type '{type}'. Convert the values to a supported type or use LINQ to Objects to order the results.</value>
</data>
<data name="LogUnexpectedConnectionType" xml:space="preserve">
<value>A connection of an unexpected type ({type}) is being used. The SQL functions prefixed with 'ef_' could not be created automatically. Manually define them if you encounter errors while querying.</value>
<comment>Warning SqliteEventId.UnexpectedConnectionTypeWarning string</comment>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ private static readonly IReadOnlyDictionary<ExpressionType, IReadOnlyCollection<
typeof(TimeSpan),
typeof(ulong)
},
[ExpressionType.Modulo] = new HashSet<Type> { typeof(decimal), typeof(ulong) },
[ExpressionType.Modulo] = new HashSet<Type>
{
typeof(ulong)
},
[ExpressionType.Multiply] = new HashSet<Type>
{
typeof(decimal),
Expand All @@ -75,6 +78,13 @@ private static readonly IReadOnlyDictionary<ExpressionType, IReadOnlyCollection<
}
};

private static readonly IReadOnlyCollection<Type> _functionModuloTypes = new HashSet<Type>
{
typeof(decimal),
typeof(double),
typeof(float)
};

public SqliteSqlTranslatingExpressionVisitor(
[NotNull] RelationalSqlTranslatingExpressionVisitorDependencies dependencies,
[NotNull] IModel model,
Expand Down Expand Up @@ -131,12 +141,30 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
return null;
}

return visitedExpression is SqlBinaryExpression sqlBinary
&& _restrictedBinaryExpressions.TryGetValue(sqlBinary.OperatorType, out var restrictedTypes)
&& (restrictedTypes.Contains(GetProviderType(sqlBinary.Left))
|| restrictedTypes.Contains(GetProviderType(sqlBinary.Right)))
? null
: visitedExpression;
if (visitedExpression is SqlBinaryExpression sqlBinary)
{
if (sqlBinary.OperatorType == ExpressionType.Modulo
&& (_functionModuloTypes.Contains(GetProviderType(sqlBinary.Left))
|| _functionModuloTypes.Contains(GetProviderType(sqlBinary.Right))))
{
return SqlExpressionFactory.Function(
"ef_mod",
new[] { sqlBinary.Left, sqlBinary.Right },
nullable: true,
argumentsPropagateNullability: new[] { true, true },
visitedExpression.Type,
visitedExpression.TypeMapping);
}

if (_restrictedBinaryExpressions.TryGetValue(sqlBinary.OperatorType, out var restrictedTypes)
&& (restrictedTypes.Contains(GetProviderType(sqlBinary.Left))
|| restrictedTypes.Contains(GetProviderType(sqlBinary.Right))))
{
return null;
}
}

return visitedExpression;
}

public override SqlExpression TranslateAverage(Expression expression)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Data.Common;
using System.Globalization;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Sqlite.Infrastructure.Internal;
using Microsoft.EntityFrameworkCore.Sqlite.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -30,6 +34,7 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal
public class SqliteRelationalConnection : RelationalConnection, ISqliteRelationalConnection
{
private readonly IRawSqlCommandBuilder _rawSqlCommandBuilder;
private readonly IDiagnosticsLogger<DbLoggerCategory.Infrastructure> _logger;
private readonly bool _loadSpatialite;
private readonly int? _commandTimeout;

Expand All @@ -41,12 +46,14 @@ public class SqliteRelationalConnection : RelationalConnection, ISqliteRelationa
/// </summary>
public SqliteRelationalConnection(
[NotNull] RelationalConnectionDependencies dependencies,
[NotNull] IRawSqlCommandBuilder rawSqlCommandBuilder)
[NotNull] IRawSqlCommandBuilder rawSqlCommandBuilder,
[NotNull] IDiagnosticsLogger<DbLoggerCategory.Infrastructure> logger)
: base(dependencies)
{
Check.NotNull(rawSqlCommandBuilder, nameof(rawSqlCommandBuilder));

_rawSqlCommandBuilder = rawSqlCommandBuilder;
_logger = logger;

var optionsExtension = dependencies.ContextOptions.Extensions.OfType<SqliteOptionsExtension>().FirstOrDefault();
if (optionsExtension != null)
Expand Down Expand Up @@ -89,7 +96,7 @@ public virtual ISqliteRelationalConnection CreateReadOnlyConnection()

var contextOptions = new DbContextOptionsBuilder().UseSqlite(connectionStringBuilder.ToString()).Options;

return new SqliteRelationalConnection(Dependencies.With(contextOptions), _rawSqlCommandBuilder);
return new SqliteRelationalConnection(Dependencies.With(contextOptions), _rawSqlCommandBuilder, _logger);
}

private void InitializeDbConnection(DbConnection connection)
Expand All @@ -99,10 +106,34 @@ private void InitializeDbConnection(DbConnection connection)
SpatialiteLoader.Load(connection);
}

if (connection is SqliteConnection sqliteConnection
&& _commandTimeout.HasValue)
if (connection is SqliteConnection sqliteConnection)
{
sqliteConnection.DefaultTimeout = _commandTimeout.Value;
if (_commandTimeout.HasValue)
{
sqliteConnection.DefaultTimeout = _commandTimeout.Value;
}

sqliteConnection.CreateFunction<object, object, object>(
"ef_mod",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, forgot to mark this as deterministic. Updating in PR #19617

(dividend, divisor) =>
{
if (dividend == null || divisor == null)
{
return null;
}
if (dividend is string s)
{
return decimal.Parse(s, CultureInfo.InvariantCulture) %
Convert.ToDecimal(divisor, CultureInfo.InvariantCulture);
}

return Convert.ToDouble(dividend, CultureInfo.InvariantCulture) %
Convert.ToDouble(divisor, CultureInfo.InvariantCulture);
});
}
else
{
_logger.UnexpectedConnectionTypeWarning(connection.GetType());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1358,6 +1358,8 @@ public virtual void Can_query_modulo_of_converted_types()
Id = 216,
PartitionId = 204,
TestDecimal = 3.000000000000003m,
TestDouble = 1.5,
TestSingle = 1.5f,
TestUnsignedInt64 = 10000000000000000001
});

Expand All @@ -1369,11 +1371,15 @@ public virtual void Can_query_modulo_of_converted_types()
{
Id = e.Id,
TestDecimal = e.TestDecimal % 2.000000000000002m,
TestDouble = e.TestDouble % 1.0,
TestSingle = e.TestSingle % 1.0f,
TestUnsignedInt64 = e.TestUnsignedInt64 % 10000000000000000000
})
.First(e => e.Id == 216);

Assert.Equal(1.000000000000001m, result.TestDecimal);
Assert.Equal(0.5, result.TestDouble);
Assert.Equal(0.5f, result.TestSingle);
Assert.Equal(1ul, result.TestUnsignedInt64);
}

Expand Down
3 changes: 2 additions & 1 deletion test/EFCore.Sqlite.Tests/SqliteEventIdTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ public void Every_eventId_has_a_logger_method_and_logs_when_level_enabled()
{
{ typeof(string), () => "Fake" },
{ typeof(IEntityType), () => entityType },
{ typeof(ISequence), () => new FakeSequence() }
{ typeof(ISequence), () => new FakeSequence() },
{ typeof(Type), () => typeof(object) }
};

TestEventLogging(
Expand Down