diff --git a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs
index 9e79413f4f0..a7e46feef10 100644
--- a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs
@@ -735,6 +735,12 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
}
}
+ // EF.Default
+ if (methodCallExpression.Method.IsEFDefaultMethod())
+ {
+ return new SqlFragmentExpression("DEFAULT");
+ }
+
var method = methodCallExpression.Method;
var arguments = methodCallExpression.Arguments;
EnumerableExpression? enumerableExpression = null;
diff --git a/src/EFCore.Sqlite.Core/Properties/SqliteStrings.Designer.cs b/src/EFCore.Sqlite.Core/Properties/SqliteStrings.Designer.cs
index 14e2b35fd63..82e837b30f6 100644
--- a/src/EFCore.Sqlite.Core/Properties/SqliteStrings.Designer.cs
+++ b/src/EFCore.Sqlite.Core/Properties/SqliteStrings.Designer.cs
@@ -37,6 +37,12 @@ public static string AggregateOperationNotSupported(object? aggregateOperator, o
public static string ApplyNotSupported
=> GetString("ApplyNotSupported");
+ ///
+ /// Translating this operation requires the 'DEFAULT', which is not supported on SQLite.
+ ///
+ public static string DefaultNotSupported
+ => GetString("DefaultNotSupported");
+
///
/// '{entityType1}.{property1}' and '{entityType2}.{property2}' are both mapped to column '{columnName}' in '{table}', but are configured with different SRIDs.
///
@@ -74,7 +80,7 @@ public static string SequencesNotSupported
=> GetString("SequencesNotSupported");
///
- /// SQLite does not support stored procedures. See http://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
+ /// SQLite does not support stored procedures, but one has been configured on entity type '{entityType}'. See http://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
///
public static string StoredProceduresNotSupported(object? entityType)
=> string.Format(
diff --git a/src/EFCore.Sqlite.Core/Properties/SqliteStrings.resx b/src/EFCore.Sqlite.Core/Properties/SqliteStrings.resx
index 89d12f3924a..f18928093f5 100644
--- a/src/EFCore.Sqlite.Core/Properties/SqliteStrings.resx
+++ b/src/EFCore.Sqlite.Core/Properties/SqliteStrings.resx
@@ -123,6 +123,9 @@
Translating this query requires the SQL APPLY operation, which is not supported on SQLite.
+
+ Translating this operation requires the 'DEFAULT', which is not supported on SQLite.
+
'{entityType1}.{property1}' and '{entityType2}.{property2}' are both mapped to column '{columnName}' in '{table}', but are configured with different SRIDs.
@@ -201,4 +204,4 @@
SQLite does not support stored procedures, but one has been configured on entity type '{entityType}'. See http://go.microsoft.com/fwlink/?LinkId=723262 for more information and examples.
-
+
\ No newline at end of file
diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs
index 665b25159d5..4977e4875f7 100644
--- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
+using Microsoft.EntityFrameworkCore.Sqlite.Internal;
namespace Microsoft.EntityFrameworkCore.Sqlite.Query.Internal;
@@ -211,6 +212,24 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
return visitedExpression;
}
+ ///
+ /// 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.
+ ///
+ protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
+ {
+ // EF.Default
+ if (methodCallExpression.Method.IsEFDefaultMethod())
+ {
+ AddTranslationErrorDetails(SqliteStrings.DefaultNotSupported);
+ return QueryCompilationContext.NotTranslatedExpression;
+ }
+
+ return base.VisitMethodCall(methodCallExpression);
+ }
+
private static Type? GetProviderType(SqlExpression? expression)
=> expression == null
? null
diff --git a/src/EFCore/EF.cs b/src/EFCore/EF.cs
index 57b650f9251..6129242cd82 100644
--- a/src/EFCore/EF.cs
+++ b/src/EFCore/EF.cs
@@ -17,6 +17,9 @@ public static partial class EF
internal static readonly MethodInfo PropertyMethod
= typeof(EF).GetTypeInfo().GetDeclaredMethod(nameof(Property))!;
+ internal static readonly MethodInfo DefaultMethod
+ = typeof(EF).GetTypeInfo().GetDeclaredMethod(nameof(Default))!;
+
///
/// This flag is set to when code is being run from a design-time tool, such
/// as "dotnet ef" or one of the Package Manager Console PowerShell commands "Add-Migration", "Update-Database", etc.
@@ -55,6 +58,20 @@ public static TProperty Property(
[NotParameterized] string propertyName)
=> throw new InvalidOperationException(CoreStrings.PropertyMethodInvoked);
+ ///
+ /// Used set a property to its default value within or
+ /// .
+ ///
+ ///
+ /// Depending on how the property is configured, this may be , or another value defined via
+ /// or similar.
+ ///
+ /// The type of the property being set.
+ /// The default value of the property.
+ public static T Default()
+ // TODO: Update exception message
+ => throw new InvalidOperationException(CoreStrings.DefaultMethodInvoked);
+
///
/// Provides CLR methods that get translated to database functions when used in LINQ to Entities queries.
/// Calling these methods in other contexts (e.g. LINQ to Objects) will throw a .
diff --git a/src/EFCore/Infrastructure/MethodInfoExtensions.cs b/src/EFCore/Infrastructure/MethodInfoExtensions.cs
index b19bd5da776..644342577a9 100644
--- a/src/EFCore/Infrastructure/MethodInfoExtensions.cs
+++ b/src/EFCore/Infrastructure/MethodInfoExtensions.cs
@@ -25,11 +25,24 @@ public static class MethodInfoExtensions
///
/// The method.
/// if the method is ; otherwise.
- public static bool IsEFPropertyMethod(this MethodInfo? methodInfo)
- => Equals(methodInfo, EF.PropertyMethod)
- // fallback to string comparison because MethodInfo.Equals is not
- // always true in .NET Native even if methods are the same
- || methodInfo?.IsGenericMethod == true
- && methodInfo.Name == nameof(EF.Property)
- && methodInfo.DeclaringType?.FullName == EFTypeName;
+ public static bool IsEFPropertyMethod(this MethodInfo methodInfo)
+ => methodInfo.IsGenericMethod
+ && (Equals(methodInfo.GetGenericMethodDefinition(), EF.PropertyMethod)
+ // fallback to string comparison because MethodInfo.Equals is not
+ // always true in .NET Native even if methods are the same
+ || (methodInfo.Name == nameof(EF.Property)
+ && methodInfo.DeclaringType?.FullName == EFTypeName));
+
+ ///
+ /// Returns if the given method is .
+ ///
+ /// The method.
+ /// if the method is ; otherwise.
+ public static bool IsEFDefaultMethod(this MethodInfo methodInfo)
+ => methodInfo.IsGenericMethod
+ && (Equals(methodInfo.GetGenericMethodDefinition(), EF.DefaultMethod)
+ // fallback to string comparison because MethodInfo.Equals is not
+ // always true in .NET Native even if methods are the same
+ || (methodInfo.Name == nameof(EF.DefaultMethod)
+ && methodInfo.DeclaringType?.FullName == EFTypeName));
}
diff --git a/src/EFCore/Properties/CoreStrings.Designer.cs b/src/EFCore/Properties/CoreStrings.Designer.cs
index 27f767ea761..de9d3009197 100644
--- a/src/EFCore/Properties/CoreStrings.Designer.cs
+++ b/src/EFCore/Properties/CoreStrings.Designer.cs
@@ -598,6 +598,12 @@ public static string DebugViewQueryStringError(object? message)
GetString("DebugViewQueryStringError", nameof(message)),
message);
+ ///
+ /// The EF.Default<T> property may only be used within Entity Framework ExecuteUpdate method.
+ ///
+ public static string DefaultMethodInvoked
+ => GetString("DefaultMethodInvoked");
+
///
/// The [DeleteBehavior] attribute may only be specified on navigation properties, and is not supported not on properties making up the foreign key.
///
diff --git a/src/EFCore/Properties/CoreStrings.resx b/src/EFCore/Properties/CoreStrings.resx
index 0ed95bdb7cf..32a8f8bbfab 100644
--- a/src/EFCore/Properties/CoreStrings.resx
+++ b/src/EFCore/Properties/CoreStrings.resx
@@ -336,6 +336,9 @@
Error creating query string: {message}.
+
+ The EF.Default<T> property may only be used within Entity Framework ExecuteUpdate method.
+
The [DeleteBehavior] attribute may only be specified on navigation properties, and is not supported not on properties making up the foreign key.
diff --git a/src/EFCore/Query/EvaluatableExpressionFilter.cs b/src/EFCore/Query/EvaluatableExpressionFilter.cs
index d3a2d0d9bf8..1dbb572dfc3 100644
--- a/src/EFCore/Query/EvaluatableExpressionFilter.cs
+++ b/src/EFCore/Query/EvaluatableExpressionFilter.cs
@@ -81,7 +81,8 @@ public virtual bool IsEvaluatableExpression(Expression expression, IModel model)
|| Equals(method, RandomNextNoArgs)
|| Equals(method, RandomNextOneArg)
|| Equals(method, RandomNextTwoArgs)
- || method.DeclaringType == typeof(DbFunctionsExtensions))
+ || method.DeclaringType == typeof(DbFunctionsExtensions)
+ || method.IsEFDefaultMethod())
{
return false;
}
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs
index 6e23350e842..38aced574f3 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs
@@ -380,6 +380,17 @@ public virtual Task Update_Where_set_constant(bool async)
rowsAffectedCount: 8,
(b, a) => Assert.All(a, c => Assert.Equal("Updated", c.ContactName)));
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_Where_set_default(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set().Where(c => c.CustomerID.StartsWith("F")),
+ e => e,
+ s => s.SetProperty(c => c.ContactName, c => EF.Default()),
+ rowsAffectedCount: 8,
+ (b, a) => Assert.All(a, c => Assert.Null(c.ContactName)));
+
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Update_Where_parameter_set_constant(bool async)
diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs
index 478adaca8a4..42ae2dac218 100644
--- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs
@@ -568,6 +568,17 @@ FROM [Customers] AS [c]
WHERE [c].[CustomerID] LIKE N'F%'");
}
+ public override async Task Update_Where_set_default(bool async)
+ {
+ await base.Update_Where_set_default(async);
+
+ AssertExecuteUpdateSql(
+ @"UPDATE [c]
+ SET [c].[ContactName] = DEFAULT
+FROM [Customers] AS [c]
+WHERE [c].[CustomerID] LIKE N'F%'");
+ }
+
public override async Task Update_Where_parameter_set_constant(bool async)
{
await base.Update_Where_parameter_set_constant(async);
diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs
index 0a1941d0a1d..785289a6102 100644
--- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore.Sqlite.Internal;
namespace Microsoft.EntityFrameworkCore.BulkUpdates;
@@ -549,6 +550,12 @@ public override async Task Update_Where_set_constant(bool async)
WHERE ""c"".""CustomerID"" LIKE 'F%'");
}
+ public override Task Update_Where_set_default(bool async)
+ => AssertTranslationFailed(
+ RelationalStrings.UnableToTranslateSetProperty(
+ "c => c.ContactName", "c => EF.Default()", SqliteStrings.DefaultNotSupported),
+ () => base.Update_Where_set_default(async));
+
public override async Task Update_Where_parameter_set_constant(bool async)
{
await base.Update_Where_parameter_set_constant(async);