Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Diese Seite dokumentiert API- und Verhaltensänderungen, die das Potenzial haben, vorhandene Anwendungen, die von EF Core 9 auf EF Core 10 aktualisiert werden, zu unterbrechen. Überprüfen Sie frühere grundlegende Änderungen, wenn sie von einer früheren Version von EF Core aktualisiert werden:
- Grundlegende Änderungen in EF Core 9
- Grundlegende Änderungen in EF Core 8
- Grundlegende Änderungen in EF Core 7
- Grundlegende Änderungen in EF Core 6
Zusammenfassung
Hinweis
Wenn Sie Microsoft.Data.Sqlite verwenden, lesen Sie den separaten Abschnitt unten über kompatibilitätsbrechende Änderungen von Microsoft.Data.Sqlite.
Änderungen mit mittlerer Auswirkung
EF-Tools erfordern jetzt die Angabe von Frameworks für mehrorientierte Projekte.
Altes Verhalten
Bisher konnten die EF-Tools (dotnet-ef) für Projekte verwendet werden, die auf mehrere Frameworks abzielen, ohne anzugeben, welches Framework verwendet werden soll.
Neues Verhalten
Ab EF Core 10.0 müssen Sie beim Ausführen von EF-Tools für ein Projekt, das auf mehrere Frameworks ausgerichtet ist (anstelle <TargetFrameworks> von <TargetFramework>), explizit angeben, welches Zielframework mit der --framework Option verwendet werden soll. Ohne diese Option wird der folgende Fehler ausgelöst:
Das Projekt zielt auf mehrere Frameworks ab. Verwenden Sie die Option --framework, um anzugeben, welches Zielframework verwendet werden soll.
Warum
In EF Core 10 haben die Tools mit der Verwendung der ResolvePackageAssets MSBuild-Aufgabe begonnen, um genauere Informationen zu Projektabhängigkeiten zu erhalten. Dieser Vorgang ist jedoch nicht verfügbar, wenn das Projekt auf mehrere Zielframeworks (TFMs) ausgerichtet ist. Für die Lösung müssen Benutzer auswählen, welches Framework verwendet werden soll.
Gegenmaßnahmen
Wenn Sie einen EF-Tools-Befehl für ein Projekt ausführen, das auf mehrere Frameworks ausgerichtet ist, geben Sie das Zielframework mithilfe der --framework Option an. Beispiel:
dotnet ef migrations add MyMigration --framework net9.0
dotnet ef database update --framework net9.0
dotnet ef migrations script --framework net9.0
Wenn die Projektdatei wie folgt aussieht:
<PropertyGroup>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
</PropertyGroup>
Sie müssen eines der Frameworks (z. B. net9.0) auswählen, wenn Sie die EF-Tools ausführen.
Änderungen mit geringer Auswirkung
Der Anwendungsname wird jetzt in die Verbindungszeichenfolge eingefügt.
Nachverfolgung von Vorfällen Nr. 35730
Neues Verhalten
Wenn eine Verbindungszeichenfolge ohne Application Name an EF übergeben wird, fügt EF jetzt ein Application Name ein, das anonyme Informationen über die verwendeten Versionen von EF und SqlClient enthält. In den meisten Fällen wirkt sich dies auf keine Weise auf die Anwendung aus, kann sich aber in einigen Spezialfällen auf das Verhalten auswirken. Wenn Sie z. B. eine Verbindung mit derselben Datenbank mit EF und einer anderen Nicht-EF-Datenzugriffstechnologie (z. B. Dapper, ADO.NET) herstellen, verwendet SqlClient einen anderen internen Verbindungspool, da EF jetzt eine andere aktualisierte Verbindungszeichenfolge verwendet (eine, in die Application Name einjiziert wurde). Wenn diese Art von gemischtem Zugriff innerhalb eines TransactionScope durchgeführt wird, kann dies zu einer verteilten Transaktion führen, obwohl zuvor keine erforderlich war. Dies liegt an der Verwendung von zwei Verbindungs-Strings, die SqlClient als zwei unterschiedliche Datenbanken identifiziert.
Gegenmaßnahmen
Eine Entschärfung besteht darin, einfach eine Application Name in Ihrer Verbindungszeichenfolge zu definieren. Sobald eine definiert ist, überschreibt EF sie nicht, und die ursprüngliche Verbindungszeichenfolge wird genau "as-is" beibehalten.
SQL Server-JSON-Datentyp, der standardmäßig in Azure SQL und Kompatibilitätsebene 170 verwendet wird
Altes Verhalten
Früher speicherte der SQL Server-Anbieter beim Zuordnen von primitiven Auflistungen oder besitzereigenen Typen zu JSON in einer nvarchar(max) Spalte die JSON-Daten:
public class Blog
{
// ...
// Primitive collection, mapped to nvarchar(max) JSON column
public string[] Tags { get; set; }
// Owned entity type mapped to nvarchar(max) JSON column
public List<Post> Posts { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().OwnsMany(b => b.Posts, b => b.ToJson());
}
Für die vorstehende Tabelle hat EF zuvor die folgende Tabelle generiert:
CREATE TABLE [Blogs] (
...
[Tags] nvarchar(max),
[Posts] nvarchar(max)
);
Neues Verhalten
Wenn Sie EF UseAzureSql mit EF 10 konfigurieren (siehe Dokumentation) oder EF mit einer Kompatibilitätsstufe von 170 oder höher konfigurieren (siehe Dokumentation), wird EF stattdessen dem neuen JSON-Datentyp zugeordnet:
CREATE TABLE [Blogs] (
...
[Tags] json
[Posts] json
);
Obwohl der neue JSON-Datentyp die empfohlene Methode zum Speichern von JSON-Daten in SQL Server in Zukunft ist, gibt es möglicherweise einige Verhaltensunterschiede beim Übergang von nvarchar(max), und einige bestimmte Abfrageformulare werden möglicherweise nicht unterstützt. Beispielsweise unterstützt SQL Server den DISTINCT-Operator nicht über JSON-Arrays, und Abfragen, die versuchen, dies zu tun, schlagen fehl.
Beachten Sie, dass bei Verwendung einer vorhandenen Tabelle ein UseAzureSqlUpgrade auf EF 10 dazu führt, dass eine Migration generiert wird, die alle vorhandenen nvarchar(max) JSON-Spalten jsonändert. Dieser Änderungsvorgang wird unterstützt und sollte nahtlos und ohne Probleme angewendet werden, ist jedoch eine nicht triviale Änderung an Ihrer Datenbank.
Warum
Der neue VON SQL Server eingeführte JSON-Datentyp ist eine überlegene, 1. Klasse zum Speichern und Interagieren mit JSON-Daten in der Datenbank; sie bringt insbesondere erhebliche Leistungsverbesserungen mit sich (siehe Dokumentation). Alle Anwendungen, die Azure SQL-Datenbank oder SQL Server 2025 verwenden, werden empfohlen, zum neuen JSON-Datentyp zu migrieren.
Gegenmaßnahmen
Wenn Sie auf azure SQL-Datenbank abzielen und nicht sofort auf den neuen JSON-Datentyp umsteigen möchten, können Sie EF mit einer Kompatibilitätsstufe unter 170 konfigurieren:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseAzureSql("<connection string>", o => o.UseCompatibilityLevel(160));
}
Wenn Sie auf lokale SQL Server abzielen, beträgt die Standardkompatibilitätsstufe UseSqlServer derzeit 150 (SQL Server 2019), sodass der JSON-Datentyp nicht verwendet wird.
Alternativ können Sie den Spaltentyp explizit auf bestimmte Eigenschaften festlegen:nvarchar(max)
public class Blog
{
public string[] Tags { get; set; }
public List<Post> Posts { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().PrimitiveCollection(b => b.Tags).HasColumnType("nvarchar(max)");
modelBuilder.Entity<Blog>().OwnsMany(b => b.Posts, b => b.ToJson().HasColumnType("nvarchar(max)"));
modelBuilder.Entity<Blog>().ComplexProperty(e => e.Posts, b => b.ToJson());
}
Parametrisierte Auflistungen verwenden jetzt standardmäßig mehrere Parameter.
Altes Verhalten
In EF Core 9 und früheren Versionen wurden parametrisierte Auflistungen in LINQ-Abfragen (z. B. die mit .Contains()) standardmäßig in SQL mit einem JSON-Arrayparameter übersetzt. Betrachten Sie die folgende Abfrage:
int[] ids = [1, 2, 3];
var blogs = await context.Blogs.Where(b => ids.Contains(b.Id)).ToListAsync();
Auf SQL Server hat dies die folgende SQL-Datei generiert:
@__ids_0='[1,2,3]'
SELECT [b].[Id], [b].[Name]
FROM [Blogs] AS [b]
WHERE [b].[Id] IN (
SELECT [i].[value]
FROM OPENJSON(@__ids_0) WITH ([value] int '$') AS [i]
)
Neues Verhalten
Ab EF Core 10.0 werden parametrisierte Auflistungen standardmäßig mit mehreren skalaren Parametern übersetzt:
SELECT [b].[Id], [b].[Name]
FROM [Blogs] AS [b]
WHERE [b].[Id] IN (@ids1, @ids2, @ids3)
Warum
Die neue Standardübersetzung bietet dem Abfrageplaner Kardinalitätsinformationen zur Sammlung, was zu besseren Abfrageplänen in vielen Szenarien führen kann. Der Ansatz mit mehreren Parametern ermöglicht eine Balance zwischen der Effizienz des Plancaches (durch die Parameterisierung) und der Optimierung von Abfragen (durch die Berücksichtigung der Kardinalität).
Je nach Sammlungsgrößen, Abfragemustern und Datenbankmerkmalen können verschiedene Workloads von unterschiedlichen Übersetzungsstrategien profitieren.
Gegenmaßnahmen
Wenn Probleme mit dem neuen Standardverhalten auftreten (z. B. Leistungsregressionen), können Sie den Übersetzungsmodus global konfigurieren:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer("<CONNECTION STRING>",
o => o.UseParameterizedCollectionMode(ParameterTranslationMode.Constant));
Verfügbare Modi sind:
-
ParameterTranslationMode.MultipleParameters- Der neue Standardwert (mehrere skalare Parameter) -
ParameterTranslationMode.Constant- Inline-Werte als Konstanten (Standardverhalten vor EF8) -
ParameterTranslationMode.Parameter- Verwendet JSON-Arrayparameter (EF8-9-Standard)
Sie können die Übersetzung auch pro Abfrage steuern:
// Use constants instead of parameters for this specific query
var blogs = await context.Blogs
.Where(b => EF.Constant(ids).Contains(b.Id))
.ToListAsync();
// Use a single parameter (e.g. JSON parameter with OPENJSON) instead of parameters for this specific query
var blogs = await context.Blogs
.Where(b => EF.Parameter(ids).Contains(b.Id))
.ToListAsync();
// Use multiple scalar parameters for this specific query. This is the default in EF 10, but is useful if the default was changed globally:
var blogs = await context.Blogs
.Where(b => EF.MultipleParameters(ids).Contains(b.Id))
.ToListAsync();
Weitere Informationen zur parametrisierten Sammlungsübersetzung finden Sie in der Dokumentation.
ExecuteUpdateAsync akzeptiert jetzt eine normale Lambda-Funktion ohne Ausdruck
Nachverfolgung von Issue 32018
Altes Verhalten
Zuvor akzeptierte ExecuteUpdate ein Ausdrucksbaum-Argument (Expression<Func<...>>) für die Spalten-Setter.
Neues Verhalten
Ab EF Core 10.0 akzeptiert ExecuteUpdate nun ein Nichtausdrucksargument (Func<...>) für die Spaltensatzer. Wenn Sie Ausdrucksbäume erstellt haben, um das Argument "Spalten-Setter" dynamisch zu erstellen, wird Ihr Code nicht mehr kompiliert, lässt sich jedoch durch eine viel einfachere Alternative ersetzen (siehe unten).
Warum
Die Tatsache, dass der Parameter "Spaltensetzer" ein Ausdrucksbaum war, machte es ziemlich schwierig, die Spaltensetzer dynamisch zu konstruieren, wobei einige Setzer nur basierend auf bestimmten Bedingungen vorhanden sind (siehe unten stehende Maßnahmen für ein Beispiel).
Gegenmaßnahmen
Code, der Ausdrucksbäume erstellt hat, um das Spalteneinstellungsargument dynamisch zu erzeugen, muss neu geschrieben werden – aber das Ergebnis wird viel einfacher sein. Angenommen, Sie möchten die Ansichten eines Blogs aktualisieren, aber bedingt auch dessen Namen. Da das Setter-Argument ein Ausdrucksbaum war, musste folgender Code geschrieben werden:
// Base setters - update the Views only
Expression<Func<SetPropertyCalls<Blog>, SetPropertyCalls<Blog>>> setters =
s => s.SetProperty(b => b.Views, 8);
// Conditionally add SetProperty(b => b.Name, "foo") to setters, based on the value of nameChanged
if (nameChanged)
{
var blogParameter = Expression.Parameter(typeof(Blog), "b");
setters = Expression.Lambda<Func<SetPropertyCalls<Blog>, SetPropertyCalls<Blog>>>(
Expression.Call(
instance: setters.Body,
methodName: nameof(SetPropertyCalls<Blog>.SetProperty),
typeArguments: [typeof(string)],
arguments:
[
Expression.Lambda<Func<Blog, string>>(Expression.Property(blogParameter, nameof(Blog.Name)), blogParameter),
Expression.Constant("foo")
]),
setters.Parameters);
}
await context.Blogs.ExecuteUpdateAsync(setters);
Das manuelle Erstellen von Ausdrucksstrukturen ist kompliziert und fehleranfällig, wodurch dieses häufige Szenario deutlich schwieriger gemacht wurde, als es eigentlich sein sollte. Ab EF 10 können Sie stattdessen Folgendes schreiben:
await context.Blogs.ExecuteUpdateAsync(s =>
{
s.SetProperty(b => b.Views, 8);
if (nameChanged)
{
s.SetProperty(b => b.Name, "foo");
}
});
Spaltennamen komplexer Typen sind jetzt eindeutig gemacht.
Altes Verhalten
Zuvor, wenn komplexe Typen Tabellenspalten zugeordnet wurden, konnten mehrere Eigenschaften in verschiedenen komplexen Typen denselben Spaltennamen aufweisen und so stillschweigend dieselbe Spalte gemeinsam nutzen.
Neues Verhalten
Beginnend mit EF Core 10.0 werden Spaltennamen für komplexe Typen eindeutig gemacht, indem eine Zahl an das Ende angefügt wird, wenn eine andere Spalte mit demselben Namen in der Tabelle vorhanden ist.
Warum
Dadurch werden Datenbeschädigungen verhindert, die auftreten können, wenn mehrere Eigenschaften unbeabsichtigt derselben Spalte zugeordnet sind.
Gegenmaßnahmen
Wenn Sie mehrere Eigenschaften für die gemeinsame Nutzung derselben Spalte benötigen, konfigurieren Sie sie explizit mithilfe Property und HasColumnName:
modelBuilder.Entity<Customer>(b =>
{
b.ComplexProperty(c => c.ShippingAddress, p => p.Property(a => a.Street).HasColumnName("Street"));
b.ComplexProperty(c => c.BillingAddress, p => p.Property(a => a.Street).HasColumnName("Street"));
});
Geschachtelte komplexe Typeigenschaften verwenden den vollständigen Pfad in Spaltennamen.
Altes Verhalten
Zuvor wurden Eigenschaften für geschachtelte komplexe Typen mit nur dem deklarierenden Typnamen auf Spalten abgebildet. Beispielsweise EntityType.Complex.NestedComplex.Property wurde der Spalte NestedComplex_Propertyzugeordnet.
Neues Verhalten
Ab EF Core 10.0 verwenden Eigenschaften für geschachtelte komplexe Typen den vollständigen Pfad zur Eigenschaft als Teil des Spaltennamens. Beispiel: EntityType.Complex.NestedComplex.Property ist jetzt Spalte Complex_NestedComplex_Propertyzugeordnet.
Warum
Dies bietet eine bessere Eindeutigkeit von Spaltennamen und macht deutlicher, welche Eigenschaft welcher Spalte zugeordnet ist.
Gegenmaßnahmen
Wenn Sie die alten Spaltennamen beibehalten müssen, konfigurieren Sie sie explizit mit Property und HasColumnName:
modelBuilder.Entity<EntityType>()
.ComplexProperty(e => e.Complex)
.ComplexProperty(o => o.NestedComplex)
.Property(c => c.Property)
.HasColumnName("NestedComplex_Property");
Die Signatur von IDiscriminatorPropertySetConvention wurde geändert.
Altes Verhalten
Zuvor nahm IDiscriminatorPropertySetConvention.ProcessDiscriminatorPropertySetIConventionEntityTypeBuilder als Parameter.
Neues Verhalten
Ab EF Core 10.0 wurde die Methodensignatur geändert, sodass sie IConventionTypeBaseBuilder anstelle von IConventionEntityTypeBuilder verwendet.
Warum
Diese Änderung ermöglicht es der Konvention, sowohl mit Entitätstypen als auch mit komplexen Typen zu arbeiten.
Gegenmaßnahmen
Aktualisieren Sie Ihre Implementierungen von benutzerdefinierten Konventionen, um die neue Signatur zu verwenden.
public virtual void ProcessDiscriminatorPropertySet(
IConventionTypeBaseBuilder typeBaseBuilder, // Changed from IConventionEntityTypeBuilder
string name,
Type type,
MemberInfo memberInfo,
IConventionContext<IConventionProperty> context)
IRelationalCommandDiagnosticsLogger-Methoden fügen logCommandText-Parameter hinzu
Altes Verhalten
Zuvor haben Methoden IRelationalCommandDiagnosticsLogger wie CommandReaderExecuting, CommandReaderExecuted, CommandScalarExecuting, und andere einen command Parameter akzeptiert, der den Datenbankbefehl darstellt, der ausgeführt wird.
Neues Verhalten
Ab EF Core 10.0 benötigen diese Methoden jetzt einen zusätzlichen logCommandText Parameter. Dieser Parameter enthält den SQL-Befehlstext, der protokolliert wird und bei dem möglicherweise vertrauliche Daten geschwärzt werden, wenn EnableSensitiveDataLogging nicht aktiviert ist.
Warum
Diese Änderung unterstützt das neue Feature, um eingebettete Konstanten standardmäßig aus der Protokollierung zu verbergen. Wenn Entity Framework Parameterwerte in SQL (z. B. bei Verwendung von EF.Constant()) einfügt, werden diese Werte jetzt in den Protokollen maskiert, es sei denn, die Protokollierung vertraulicher Daten ist explizit aktiviert. Der logCommandText Parameter stellt die redacted SQL für Protokollierungszwecke bereit, während der command Parameter den tatsächlichen SQL-Wert enthält, der ausgeführt wird.
Gegenmaßnahmen
Wenn Sie über eine benutzerdefinierte Implementierung IRelationalCommandDiagnosticsLogger verfügen, müssen Sie die Methodensignaturen aktualisieren, um den neuen Parameter logCommandText einzuschließen. Beispiel:
public InterceptionResult<DbDataReader> CommandReaderExecuting(
IRelationalConnection connection,
DbCommand command,
DbContext context,
Guid commandId,
Guid connectionId,
DateTimeOffset startTime,
string logCommandText) // New parameter
{
// Use logCommandText for logging purposes
// Use command for execution-related logic
}
Der logCommandText Parameter enthält die SQL-Datei, die protokolliert werden soll (mit inlineierten Konstanten, die potenziell redigiert sind), und command.CommandText enthält den tatsächlichen SQL-Wert, der für die Datenbank ausgeführt wird.
Microsoft.Data.Sqlite – Wichtige Änderungen
Zusammenfassung
Änderungen mit hoher Auswirkung
Beim Verwenden von GetDateTimeOffset ohne einen Offset wird jetzt davon ausgegangen, dass UTC verwendet wird.
Nachverfolgung von Issue 36195
Altes Verhalten
Wenn GetDateTimeOffset auf einen Textzeitstempel angewendet wird, der keinen Offset (z. B. 2014-04-15 10:47:16) aufweist, nimmt Microsoft.Data.Sqlite an, dass der Wert in der lokalen Zeitzone liegt. D.h. der Wert wurde analysiert als 2014-04-15 10:47:16+02:00 (vorausgesetzt, die lokale Zeitzone war UTC+2).
Neues Verhalten
Ab Microsoft.Data.Sqlite 10.0 wird bei Verwendung GetDateTimeOffset eines textbezogenen Zeitstempels, der keinen Offset aufweist, von Microsoft.Data.Sqlite vorausgesetzt, dass der Wert in UTC liegt.
Warum
Entspricht dem Verhalten von SQLite, bei dem Zeitstempel ohne Offset als UTC behandelt werden.
Gegenmaßnahmen
Der Code sollte entsprechend angepasst werden.
Als letztes/temporäres Mittel können Sie zum vorherigen Verhalten zurückkehren, indem Sie den AppContext-Schalter auf Microsoft.Data.Sqlite.Pre10TimeZoneHandling"AppContext" festlegentrue. Weitere Informationen finden Sie unter AppContext für Bibliothekskunden.
AppContext.SetSwitch("Microsoft.Data.Sqlite.Pre10TimeZoneHandling", isEnabled: true);
Schreiben von DateTimeOffset in REAL-Spalte wird jetzt in UTC geschrieben
Nachverfolgung von Issue 36195
Altes Verhalten
Früher, beim Schreiben eines DateTimeOffset Werts in eine REAL-Spalte, schrieb Microsoft.Data.Sqlite den Wert, ohne den Offset zu berücksichtigen.
Neues Verhalten
Ab Microsoft.Data.Sqlite 10.0 konvertiert Microsoft.Data.Sqlite beim Schreiben eines DateTimeOffset Werts in eine REAL-Spalte den Wert in UTC, bevor die Konvertierungen ausgeführt und geschrieben werden.
Warum
Der geschriebene Wert war falsch und entsprach nicht dem Verhalten von SQLite, bei dem davon ausgegangen wird, dass REAL-Zeitstempel UTC sind.
Gegenmaßnahmen
Der Code sollte entsprechend angepasst werden.
Als letztes/temporäres Mittel können Sie zum vorherigen Verhalten zurückkehren, indem Sie den AppContext-Schalter auf Microsoft.Data.Sqlite.Pre10TimeZoneHandling"AppContext" festlegentrue. Weitere Informationen finden Sie unter AppContext für Bibliothekskunden.
AppContext.SetSwitch("Microsoft.Data.Sqlite.Pre10TimeZoneHandling", isEnabled: true);
Die Verwendung von GetDateTime mit einem Offset gibt jetzt den Wert in UTC zurück.
Nachverfolgung von Issue 36195
Altes Verhalten
Früher, wenn GetDateTime auf einen Textzeitstempel mit einem Offset (z. B. 2014-04-15 10:47:16+02:00) angewendet wurde, gab Microsoft.Data.Sqlite den Wert mit DateTimeKind.Local zurück, selbst wenn der Offset nicht lokal war. Die Zeit wurde unter Berücksichtigung des Offsets richtig analysiert.
Neues Verhalten
Ab Microsoft.Data.Sqlite 10.0 wird bei der Verwendung von GetDateTime auf einen Textzeitstempel mit einem Offset Microsoft.Data.Sqlite den Wert in UTC konvertieren und ihn mit DateTimeKind.Utc zurückgeben.
Warum
Obwohl die Zeit richtig analysiert wurde, hängt sie von der vom Computer konfigurierten lokalen Zeitzone ab, was zu unerwarteten Ergebnissen führen könnte.
Gegenmaßnahmen
Der Code sollte entsprechend angepasst werden.
Als letztes/temporäres Mittel können Sie zum vorherigen Verhalten zurückkehren, indem Sie den AppContext-Schalter auf Microsoft.Data.Sqlite.Pre10TimeZoneHandling"AppContext" festlegentrue. Weitere Informationen finden Sie unter AppContext für Bibliothekskunden.
AppContext.SetSwitch("Microsoft.Data.Sqlite.Pre10TimeZoneHandling", isEnabled: true);