diff --git a/.editorconfig b/.editorconfig index 386d669b..5d097a29 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,6 +5,7 @@ indent_style = tab indent_size = 4 insert_final_newline = true end_of_line = crlf +spelling_exclusion_path = spellcheck.txt # Code files [*.{cs,csx,vb,vbx}] @@ -160,15 +161,18 @@ dotnet_diagnostic.CA3076.severity = none # CA3076: Insecure XSLT Script Executio ######################################################################################################### dotnet_diagnostic.CA1000.severity = none # CA1000: Do not declare static members on generic types dotnet_diagnostic.CA1001.severity = none # CA1001: Types that own disposable fields should be disposable +dotnet_diagnostic.CA1002.severity = none # CA1002: Do not expose generic lists dotnet_diagnostic.CA1010.severity = none # CA1010: Collections should implement generic interface +dotnet_diagnostic.CA1033.severity = none # CA1033: Interface methods should be callable by child types dotnet_diagnostic.CA1050.severity = none # CA1050: Declare types in namespaces dotnet_diagnostic.CA1036.severity = none # CA1036: Override methods on comparable types dotnet_diagnostic.CA1051.severity = none # CA1051: Do not declare visible instance fields +dotnet_diagnostic.CA1056.severity = none # CA1056: URI properties should not be strings +dotnet_diagnostic.CA1062.severity = none # CA1062: Validate arguments of public methods dotnet_diagnostic.CA1067.severity = none # CA1067: Override Equals when implementing IEquatable dotnet_diagnostic.CA1068.severity = none # CA1068: CancellationToken parameters must come last dotnet_diagnostic.CA1069.severity = none # CA1069: Enums should not have duplicate values -dotnet_diagnostic.CA1304.severity = none # CA1304: Specify CultureInfo -dotnet_diagnostic.CA1305.severity = none # CA1305: Specify IFormatProvider +dotnet_diagnostic.CA1307.severity = none # CA1307: Specify StringComparison for clarity dotnet_diagnostic.CA1309.severity = none # CA1309: Use ordinal StringComparison dotnet_diagnostic.CA1310.severity = none # CA1310: Specify StringComparison for correctness dotnet_diagnostic.CA1707.severity = none # CA1707: Identifiers should not contain underscores @@ -178,8 +182,12 @@ dotnet_diagnostic.CA1715.severity = none # CA1715: Identifiers should have corre dotnet_diagnostic.CA1716.severity = none # CA1716: Identifiers should not match keywords dotnet_diagnostic.CA1720.severity = none # CA1720: Identifiers should not contain type names dotnet_diagnostic.CA1806.severity = none # CA1806: Do not ignore method results +dotnet_diagnostic.CA1819.severity = none # CA1819: Properties should not return arrays dotnet_diagnostic.CA1822.severity = none # CA1822: Mark members as static +dotnet_diagnostic.CA1859.severity = none # CA1859: Use concrete types when possible for improved performance dotnet_diagnostic.CA2211.severity = none # CA2211: Non-constant fields should not be visible +dotnet_diagnostic.CA2225.severity = none # CA2225: Operator overloads have named alternates +dotnet_diagnostic.CA2227.severity = none # CA2227: Remove property setter ################################################################################### # VS analyzers # @@ -250,10 +258,324 @@ dotnet_diagnostic.IDE0080.severity = none # IDE0080: Remove unnecessary suppress dotnet_diagnostic.IDE0081.severity = none # IDE0081: Remove ByVal dotnet_diagnostic.IDE0083.severity = none # IDE0083: Use pattern matching (not operator) dotnet_diagnostic.IDE0130.severity = none # IDE0130: Namespace does not match folder structure +dotnet_diagnostic.IDE0290.severity = none # IDE0290: Use primary constructor +dotnet_diagnostic.IDE0305.severity = none # IDE0305: Use collection expression (from ToArray) dotnet_diagnostic.IDE1006.severity = none # IDE1006: Naming rule violation dotnet_diagnostic.CS1998.severity = error # CS1998: Async method lacks 'await' operators and will run synchronously dotnet_diagnostic.CS8618.severity = error # CS8618: Non-nullable field is uninitialized. Consider declaring as nullable. dotnet_diagnostic.CS4014.severity = error # CS4014: Because this call is not awaited, execution of the current method continues before the call is completed -# +# do not disable rules below +dotnet_diagnostic.CA1304.severity = error # CA1304: Specify CultureInfo +dotnet_diagnostic.CA1305.severity = error # CA1305: Specify IFormatProvider +dotnet_code_quality.CA1305.excluded_symbol_names = M:System.Convert.ToString(System.Boolean)|M:System.Convert.ToChar(System.String)|M:System.Convert.ToBoolean(System.String) + +###### Meziantou.Analyzers + +# MA0001: StringComparison is missing +dotnet_diagnostic.MA0001.severity = none +# MA0002: IEqualityComparer or IComparer is missing +dotnet_diagnostic.MA0002.severity = none +# MA0003: Add parameter name to improve readability +dotnet_diagnostic.MA0003.severity = none +# MA0004: Use Task.ConfigureAwait(false) +dotnet_diagnostic.MA0004.severity = none +# MA0005: Use Array.Empty() +dotnet_diagnostic.MA0005.severity = none +# MA0006: Use String.Equals instead of equality operator +dotnet_diagnostic.MA0006.severity = none +# MA0007: Add a comma after the last value +dotnet_diagnostic.MA0007.severity = none +# MA0008: Add StructLayoutAttribute +dotnet_diagnostic.MA0008.severity = none +# MA0009: Add regex evaluation timeout +dotnet_diagnostic.MA0009.severity = none +# MA0010: Mark attributes with AttributeUsageAttribute +dotnet_diagnostic.MA0010.severity = none +# MA0011: IFormatProvider is missing +dotnet_diagnostic.MA0011.severity = error +# MA0012: Do not raise reserved exception type +dotnet_diagnostic.MA0012.severity = none +# MA0013: Types should not extend System.ApplicationException +dotnet_diagnostic.MA0013.severity = none +# MA0014: Do not raise System.ApplicationException type +dotnet_diagnostic.MA0014.severity = none +# MA0015: Specify the parameter name in ArgumentException +dotnet_diagnostic.MA0015.severity = none +# MA0016: Prefer using collection abstraction instead of implementation +dotnet_diagnostic.MA0016.severity = none +# MA0017: Abstract types should not have public or internal constructors +dotnet_diagnostic.MA0017.severity = none +# MA0018: Do not declare static members on generic types (deprecated; use CA1000 instead) +dotnet_diagnostic.MA0018.severity = none +# MA0019: Use EventArgs.Empty +dotnet_diagnostic.MA0019.severity = none +# MA0020: Use direct methods instead of LINQ methods +dotnet_diagnostic.MA0020.severity = none +# MA0021: Use StringComparer.GetHashCode instead of string.GetHashCode +dotnet_diagnostic.MA0021.severity = none +# MA0022: Return Task.FromResult instead of returning null +dotnet_diagnostic.MA0022.severity = none +# MA0023: Add RegexOptions.ExplicitCapture +dotnet_diagnostic.MA0023.severity = none +# MA0024: Use an explicit StringComparer when possible +dotnet_diagnostic.MA0024.severity = none +# MA0025: Implement the functionality instead of throwing NotImplementedException +dotnet_diagnostic.MA0025.severity = none +# MA0026: Fix TODO comment +dotnet_diagnostic.MA0026.severity = none +# MA0027: Prefer rethrowing an exception implicitly +dotnet_diagnostic.MA0027.severity = none +# MA0028: Optimize StringBuilder usage +dotnet_diagnostic.MA0028.severity = none +# MA0029: Combine LINQ methods +dotnet_diagnostic.MA0029.severity = none +# MA0030: Remove useless OrderBy call +dotnet_diagnostic.MA0030.severity = none +# MA0031: Optimize Enumerable.Count() usage +dotnet_diagnostic.MA0031.severity = none +# MA0032: Use an overload with a CancellationToken argument +dotnet_diagnostic.MA0032.severity = none +# MA0033: Do not tag instance fields with ThreadStaticAttribute +dotnet_diagnostic.MA0033.severity = none +# MA0035: Do not use dangerous threading methods +dotnet_diagnostic.MA0035.severity = none +# MA0036: Make class static +dotnet_diagnostic.MA0036.severity = none +# MA0037: Remove empty statement +dotnet_diagnostic.MA0037.severity = none +# MA0038: Make method static (deprecated, use CA1822 instead) +dotnet_diagnostic.MA0038.severity = none +# MA0039: Do not write your own certificate validation method +dotnet_diagnostic.MA0039.severity = none +# MA0040: Forward the CancellationToken parameter to methods that take one +dotnet_diagnostic.MA0040.severity = none +# MA0041: Make property static (deprecated, use CA1822 instead) +dotnet_diagnostic.MA0041.severity = none +# MA0042: Do not use blocking calls in an async method +dotnet_diagnostic.MA0042.severity = none +# MA0043: Use nameof operator in ArgumentException +dotnet_diagnostic.MA0043.severity = none +# MA0044: Remove useless ToString call +dotnet_diagnostic.MA0044.severity = error +# MA0045: Do not use blocking calls in a sync method (need to make calling method async) +dotnet_diagnostic.MA0045.severity = none +# MA0046: Use EventHandler to declare events +dotnet_diagnostic.MA0046.severity = none +# MA0047: Declare types in namespaces +dotnet_diagnostic.MA0047.severity = none +# MA0048: File name must match type name +dotnet_diagnostic.MA0048.severity = none +# MA0049: Type name should not match containing namespace +dotnet_diagnostic.MA0049.severity = none +# MA0050: Validate arguments correctly in iterator methods +dotnet_diagnostic.MA0050.severity = none +# MA0051: Method is too long +dotnet_diagnostic.MA0051.severity = none +# MA0052: Replace constant Enum.ToString with nameof +dotnet_diagnostic.MA0052.severity = none +# MA0053: Make class sealed +dotnet_diagnostic.MA0053.severity = none +# MA0054: Embed the caught exception as innerException +dotnet_diagnostic.MA0054.severity = none +# MA0055: Do not use finalizer +dotnet_diagnostic.MA0055.severity = none +# MA0056: Do not call overridable members in constructor +dotnet_diagnostic.MA0056.severity = none +# MA0057: Class name should end with 'Attribute' +dotnet_diagnostic.MA0057.severity = none +# MA0058: Class name should end with 'Exception' +dotnet_diagnostic.MA0058.severity = none +# MA0059: Class name should end with 'EventArgs' +dotnet_diagnostic.MA0059.severity = none +# MA0060: The value returned by Stream.Read/Stream.ReadAsync is not used +dotnet_diagnostic.MA0060.severity = none +# MA0061: Method overrides should not change default values +dotnet_diagnostic.MA0061.severity = none +# MA0062: Non-flags enums should not be marked with "FlagsAttribute" +dotnet_diagnostic.MA0062.severity = none +# MA0063: Use Where before OrderBy +dotnet_diagnostic.MA0063.severity = none +# MA0064: Avoid locking on publicly accessible instance +dotnet_diagnostic.MA0064.severity = none +# MA0065: Default ValueType.Equals or HashCode is used for struct equality +dotnet_diagnostic.MA0065.severity = none +# MA0066: Hash table unfriendly type is used in a hash table +dotnet_diagnostic.MA0066.severity = none +# MA0067: Use Guid.Empty +dotnet_diagnostic.MA0067.severity = none +# MA0068: Invalid parameter name for nullable attribute +dotnet_diagnostic.MA0068.severity = none +# MA0069: Non-constant static fields should not be visible +dotnet_diagnostic.MA0069.severity = none +# MA0070: Obsolete attributes should include explanations +dotnet_diagnostic.MA0070.severity = none +# MA0071: Avoid using redundant else +dotnet_diagnostic.MA0071.severity = none +# MA0072: Do not throw from a finally block +dotnet_diagnostic.MA0072.severity = none +# MA0073: Avoid comparison with bool constant +dotnet_diagnostic.MA0073.severity = none +# MA0074: Avoid implicit culture-sensitive methods +dotnet_diagnostic.MA0074.severity = none +# MA0075: Do not use implicit culture-sensitive ToString +dotnet_diagnostic.MA0075.severity = error +# MA0076: Do not use implicit culture-sensitive ToString in interpolated strings +dotnet_diagnostic.MA0076.severity = error +# MA0077: A class that provides Equals(T) should implement IEquatable +dotnet_diagnostic.MA0077.severity = none +# MA0078: Use 'Cast' instead of 'Select' to cast +dotnet_diagnostic.MA0078.severity = none +# MA0079: Forward the CancellationToken using .WithCancellation() +dotnet_diagnostic.MA0079.severity = none +# MA0080: Use a cancellation token using .WithCancellation() +dotnet_diagnostic.MA0080.severity = none +# MA0081: Method overrides should not omit params keyword +dotnet_diagnostic.MA0081.severity = none +# MA0082: NaN should not be used in comparisons +dotnet_diagnostic.MA0082.severity = none +# MA0083: ConstructorArgument parameters should exist in constructors +dotnet_diagnostic.MA0083.severity = none +# MA0084: Local variables should not hide other symbols +dotnet_diagnostic.MA0084.severity = none +# MA0085: Anonymous delegates should not be used to unsubscribe from Events +dotnet_diagnostic.MA0085.severity = none +# MA0086: Do not throw from a finalizer +dotnet_diagnostic.MA0086.severity = none +# MA0087: Parameters with [DefaultParameterValue] attributes should also be marked [Optional] +dotnet_diagnostic.MA0087.severity = none +# MA0088: Use [DefaultParameterValue] instead of [DefaultValue] +dotnet_diagnostic.MA0088.severity = none +# MA0089: Optimize string method usage +dotnet_diagnostic.MA0089.severity = none +# MA0090: Remove empty else/finally block +dotnet_diagnostic.MA0090.severity = none +# MA0091: Sender should be 'this' for instance events +dotnet_diagnostic.MA0091.severity = none +# MA0092: Sender should be 'null' for static events +dotnet_diagnostic.MA0092.severity = none +# MA0093: EventArgs should not be null +dotnet_diagnostic.MA0093.severity = none +# MA0094: A class that provides CompareTo(T) should implement IComparable +dotnet_diagnostic.MA0094.severity = none +# MA0095: A class that implements IEquatable should override Equals(object) +dotnet_diagnostic.MA0095.severity = none +# MA0096: A class that implements IComparable should also implement IEquatable +dotnet_diagnostic.MA0096.severity = none +# MA0097: A class that implements IComparable or IComparable should override comparison operators +dotnet_diagnostic.MA0097.severity = none +# MA0098: Use indexer instead of LINQ methods +dotnet_diagnostic.MA0098.severity = none +# MA0099: Use Explicit enum value instead of 0 +dotnet_diagnostic.MA0099.severity = none +# MA0100: Await task before disposing of resources +dotnet_diagnostic.MA0100.severity = none +# MA0101: String contains an implicit end of line character +dotnet_diagnostic.MA0101.severity = none +# MA0102: Make member readonly +dotnet_diagnostic.MA0102.severity = none +# MA0103: Use SequenceEqual instead of equality operator +dotnet_diagnostic.MA0103.severity = none +# MA0104: Do not create a type with a name from the BCL +dotnet_diagnostic.MA0104.severity = none +# MA0105: Use the lambda parameters instead of using a closure +dotnet_diagnostic.MA0105.severity = none +# MA0106: Avoid closure by using an overload with the 'factoryArgument' parameter +dotnet_diagnostic.MA0106.severity = none +# MA0107: Do not use culture-sensitive object.ToString +dotnet_diagnostic.MA0107.severity = error +# MA0108: Remove redundant argument value +dotnet_diagnostic.MA0108.severity = none +# MA0109: Consider adding an overload with a Span or Memory +dotnet_diagnostic.MA0109.severity = none +# MA0110: Use the Regex source generator +dotnet_diagnostic.MA0110.severity = none +# MA0111: Use string.Create instead of FormattableString +dotnet_diagnostic.MA0111.severity = none +# MA0112: Use 'Count > 0' instead of 'Any()' +dotnet_diagnostic.MA0112.severity = none +# MA0113: Use DateTime.UnixEpoch +dotnet_diagnostic.MA0113.severity = none +# MA0114: Use DateTimeOffset.UnixEpoch +dotnet_diagnostic.MA0114.severity = none +# MA0115: Unknown component parameter +dotnet_diagnostic.MA0115.severity = none +# MA0116: Parameters with [SupplyParameterFromQuery] attributes should also be marked as [Parameter] +dotnet_diagnostic.MA0116.severity = none +# MA0117: Parameters with [EditorRequired] attributes should also be marked as [Parameter] +dotnet_diagnostic.MA0117.severity = none +# MA0118: [JSInvokable] methods must be public +dotnet_diagnostic.MA0118.severity = none +# MA0119: JSRuntime must not be used in OnInitialized or OnInitializedAsync +dotnet_diagnostic.MA0119.severity = none +# MA0120: Use InvokeVoidAsync when the returned value is not used +dotnet_diagnostic.MA0120.severity = none +# MA0121: Do not overwrite parameter value +dotnet_diagnostic.MA0121.severity = none +# MA0122: Parameters with [SupplyParameterFromQuery] attributes are only valid in routable components (@page) +dotnet_diagnostic.MA0122.severity = none +# MA0123: Sequence number must be a constant +dotnet_diagnostic.MA0123.severity = none +# MA0124: Log Parameter type is not valid +dotnet_diagnostic.MA0124.severity = none +# MA0125: The list of log parameter types contains an invalid type +dotnet_diagnostic.MA0125.severity = none +# MA0126: The list of log parameter types contains a duplicate +dotnet_diagnostic.MA0126.severity = none +# MA0127: Use String.Equals instead of is pattern +dotnet_diagnostic.MA0127.severity = none +# MA0128: Use 'is' operator instead of SequenceEqual +dotnet_diagnostic.MA0128.severity = none +# MA0129: Await task in using statement +dotnet_diagnostic.MA0129.severity = none +# MA0130: GetType() should not be used on System.Type instances +dotnet_diagnostic.MA0130.severity = none +# MA0131: ArgumentNullException.ThrowIfNull should not be used with non-nullable types +dotnet_diagnostic.MA0131.severity = none +# MA0132: Do not convert implicitly to DateTimeOffset +dotnet_diagnostic.MA0132.severity = none +# MA0133: Use DateTimeOffset instead of relying on the implicit conversion +dotnet_diagnostic.MA0133.severity = none +# MA0134: Observe result of async calls +dotnet_diagnostic.MA0134.severity = none +# MA0135: The log parameter has no configured type +dotnet_diagnostic.MA0135.severity = none +# MA0136: Raw String contains an implicit end of line character +dotnet_diagnostic.MA0136.severity = none +# MA0137: Use 'Async' suffix when a method returns an awaitable type +dotnet_diagnostic.MA0137.severity = none +# MA0138: Do not use 'Async' suffix when a method does not return an awaitable type +dotnet_diagnostic.MA0138.severity = none +# MA0139: Log Parameter type is not valid +dotnet_diagnostic.MA0139.severity = none +# MA0140: Both if and else branch have identical code +dotnet_diagnostic.MA0140.severity = none +# MA0141: Use pattern matching instead of inequality operators for null check +dotnet_diagnostic.MA0141.severity = none +# MA0142: Use pattern matching instead of equality operators for null check +dotnet_diagnostic.MA0142.severity = none +# MA0143: Primary constructor parameters should be readonly +dotnet_diagnostic.MA0143.severity = none +# MA0144: Use System.OperatingSystem to check the current OS +dotnet_diagnostic.MA0144.severity = none +# MA0145: Signature for [UnsafeAccessorAttribute] method is not valid +dotnet_diagnostic.MA0145.severity = none +# MA0146: Name must be set explicitly on local functions +dotnet_diagnostic.MA0146.severity = none +# MA0147: Avoid async void method for delegate +dotnet_diagnostic.MA0147.severity = none +# MA0148: Use pattern matching instead of equality operators for discrete value +dotnet_diagnostic.MA0148.severity = none +# MA0149: Use pattern matching instead of inequality operators for discrete value +dotnet_diagnostic.MA0149.severity = none +# MA0150: Do not call the default object.ToString explicitly +dotnet_diagnostic.MA0150.severity = none +# MA0151: DebuggerDisplay must contain valid members +dotnet_diagnostic.MA0151.severity = none +# MA0152: Use Unwrap instead of using await twice +dotnet_diagnostic.MA0152.severity = none +# MA0153: Do not log symbols decorated with DataClassificationAttribute directly +dotnet_diagnostic.MA0153.severity = none +# MA0154: Use langword in XML comment +dotnet_diagnostic.MA0154.severity = none diff --git a/Directory.Build.props b/Directory.Build.props index c82e5756..5bd66ed5 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,11 +1,11 @@  - 2.13.0 + 2.14.0 Svyatoslav Danyliv, Igor Tkachev, Dmitry Lukashenko, Ilya Chudin Linq to DB linq2db.net - 2002-2023 linq2db.net + 2002-2024 linq2db.net https://github.com/linq2db/linq2db.EntityFrameworkCore git @@ -29,7 +29,7 @@ true true false - true + true true diff --git a/Directory.Packages.props b/Directory.Packages.props index f891a3a2..90da9705 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,24 +1,26 @@  - + - + - - + + + + + + + - - - diff --git a/NuGet/icon.png b/NuGet/icon.png index a6496e4b..8df3a31c 100644 Binary files a/NuGet/icon.png and b/NuGet/icon.png differ diff --git a/NuGet/linq2db.EntityFrameworkCore.nuspec b/NuGet/linq2db.EntityFrameworkCore.nuspec index f803c6ac..5e66038f 100644 --- a/NuGet/linq2db.EntityFrameworkCore.nuspec +++ b/NuGet/linq2db.EntityFrameworkCore.nuspec @@ -5,7 +5,7 @@ Linq to DB (linq2db) extensions for Entity Framework Core Igor Tkachev, Ilya Chudin, Svyatoslav Danyliv, Dmitry Lukashenko Igor Tkachev, Ilya Chudin, Svyatoslav Danyliv, Dmitry Lukashenko - Copyright © 2020-2023 Igor Tkachev, Ilya Chudin, Svyatoslav Danyliv, Dmitry Lukashenko + Copyright © 2020-2024 Igor Tkachev, Ilya Chudin, Svyatoslav Danyliv, Dmitry Lukashenko Allows to execute Linq to DB (linq2db) queries in Entity Framework Core DbContext. linq linq2db LinqToDB ORM database entity-framework-core EntityFrameworkCore EFCore DB SQL SqlServer SqlCe SqlServerCe MySql Firebird SQLite Oracle ODP PostgreSQL DB2 @@ -15,7 +15,7 @@ MIT-LICENSE.txt - + diff --git a/Source/LinqToDB.EntityFrameworkCore/Compatibility/NullableAttributes.cs b/Source/LinqToDB.EntityFrameworkCore/Compatibility/NullableAttributes.cs deleted file mode 100644 index 33d6ad97..00000000 --- a/Source/LinqToDB.EntityFrameworkCore/Compatibility/NullableAttributes.cs +++ /dev/null @@ -1,148 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs -// https://raw.githubusercontent.com/dotnet/runtime/master/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs -namespace System.Diagnostics.CodeAnalysis -{ - /// Specifies that null is allowed as an input even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] - sealed class AllowNullAttribute : Attribute - { } - - /// Specifies that null is disallowed as an input even if the corresponding type allows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] - sealed class DisallowNullAttribute : Attribute - { } - - /// Specifies that an output may be null even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] - sealed class MaybeNullAttribute : Attribute - { } - - /// Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input argument was not null when the call returns. - [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] - sealed class NotNullAttribute : Attribute - { } - - /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - sealed class MaybeNullWhenAttribute : Attribute - { - /// Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter may be null. - /// - public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// Gets the return value condition. - public bool ReturnValue { get; } - } - - /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - sealed class NotNullWhenAttribute : Attribute - { - /// Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// Gets the return value condition. - public bool ReturnValue { get; } - } - - /// Specifies that the output will be non-null if the named parameter is non-null. - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] - sealed class NotNullIfNotNullAttribute : Attribute - { - /// Initializes the attribute with the associated parameter name. - /// - /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. - /// - public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; - - /// Gets the associated parameter name. - public string ParameterName { get; } - } - - /// Applied to a method that will never return under any circumstance. - [AttributeUsage(AttributeTargets.Method, Inherited = false)] - sealed class DoesNotReturnAttribute : Attribute - { } - - /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - sealed class DoesNotReturnIfAttribute : Attribute - { - /// Initializes the attribute with the specified parameter value. - /// - /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to - /// the associated parameter matches this value. - /// - public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; - - /// Gets the condition parameter value. - public bool ParameterValue { get; } - } - - /// Specifies that the method or property will ensure that the listed field and property members have not-null values. - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - sealed class MemberNotNullAttribute : Attribute - { - /// Initializes the attribute with a field or property member. - /// - /// The field or property member that is promised to be not-null. - /// - public MemberNotNullAttribute(string member) => Members = new[] { member }; - - /// Initializes the attribute with the list of field and property members. - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullAttribute(params string[] members) - => Members = members; - - /// Gets field or property member names. - public string[] Members { get; } - } - - /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] - sealed class MemberNotNullWhenAttribute : Attribute - { - /// Initializes the attribute with the specified return value condition and a field or property member. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - /// - /// The field or property member that is promised to be not-null. - /// - public MemberNotNullWhenAttribute(bool returnValue, string member) - { - ReturnValue = returnValue; - Members = new[] { member }; - } - - /// Initializes the attribute with the specified return value condition and list of field and property members. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullWhenAttribute(bool returnValue, params string[] members) - { - ReturnValue = returnValue; - Members = members; - } - - /// Gets the return value condition. - public bool ReturnValue { get; } - - /// Gets field or property member names. - public string[] Members { get; } - } -} diff --git a/Source/LinqToDB.EntityFrameworkCore/EFCoreMetadataReader.cs b/Source/LinqToDB.EntityFrameworkCore/EFCoreMetadataReader.cs index dfda231c..7901ab86 100644 --- a/Source/LinqToDB.EntityFrameworkCore/EFCoreMetadataReader.cs +++ b/Source/LinqToDB.EntityFrameworkCore/EFCoreMetadataReader.cs @@ -49,7 +49,7 @@ public EFCoreMetadataReader(IModel? model, IInfrastructure? ac _annotationProvider = accessor.GetService(); } - _objectId = $".{_model?.GetHashCode() ?? 0}.{_dependencies?.GetHashCode() ?? 0}.{_mappingSource?.GetHashCode() ?? 0}.{_annotationProvider?.GetHashCode() ?? 0}."; + _objectId = FormattableString.Invariant($".{_model?.GetHashCode() ?? 0}.{_dependencies?.GetHashCode() ?? 0}.{_mappingSource?.GetHashCode() ?? 0}.{_annotationProvider?.GetHashCode() ?? 0}."); } public MappingAttribute[] GetAttributes(Type type) @@ -121,10 +121,10 @@ public MappingAttribute[] GetAttributes(Type type) // TableAttribute var tableAttribute = type.GetAttribute(); if (tableAttribute != null) - (result ??= new()).Add(new TableAttribute(tableAttribute.Name) { Schema = tableAttribute.Schema }); + (result = new()).Add(new TableAttribute(tableAttribute.Name) { Schema = tableAttribute.Schema }); } - return result == null ? Array.Empty() : result.ToArray(); + return result == null ? [] : result.ToArray(); } static IEntityType GetBaseTypeRecursive(IEntityType entityType) @@ -218,7 +218,7 @@ static DataType DbTypeToDataType(DbType dbType) public MappingAttribute[] GetAttributes(Type type, MemberInfo memberInfo) { if (typeof(Expression).IsSameOrParentOf(type)) - return Array.Empty(); + return []; List? result = null; var hasColumn = false; @@ -256,7 +256,7 @@ public MappingAttribute[] GetAttributes(Type type, MemberInfo memberInfo) var annotations = prop.GetAnnotations(); if (_annotationProvider != null) { - annotations = annotations.Concat(_annotationProvider.For(prop)); + annotations = annotations.Concat(_annotationProvider.For(prop)).ToArray(); } var isIdentity = annotations @@ -264,7 +264,7 @@ public MappingAttribute[] GetAttributes(Type type, MemberInfo memberInfo) { if (a.Name.EndsWith(":ValueGenerationStrategy")) { - var value = a.Value?.ToString(); + var value = a.Value == null ? null : FormattableString.Invariant($"{a.Value}"); if (value != null && (value.Contains("Identity") || value.Contains("Serial"))) return true; @@ -278,7 +278,9 @@ public MappingAttribute[] GetAttributes(Type type, MemberInfo memberInfo) { if (a.Value is string str) { - return str.ToLowerInvariant().Contains("nextval"); +#pragma warning disable CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string comparisons + return str.ToUpperInvariant().Contains("NEXTVAL"); +#pragma warning restore CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string comparisons } } @@ -437,7 +439,7 @@ public MappingAttribute[] GetAttributes(Type type, MemberInfo memberInfo) // }); //} - return result == null ? Array.Empty() : result.ToArray(); + return result == null ? [] : result.ToArray(); } sealed class ValueConverter : IValueConverter @@ -587,7 +589,7 @@ string PrepareExpressionText(Expression? expr) } if (idx >= 0) - return $"{{{idx}}}"; + return FormattableString.Invariant($"{{{idx}}}"); if (expr is SqlFragmentExpression fragment) return fragment.Sql; @@ -600,16 +602,16 @@ string PrepareExpressionText(Expression? expr) if (!sqlFunction.IsNiladic) { - text = text + "("; + text += "("; for (var i = 0; i < sqlFunction.Arguments.Count; i++) { var paramText = PrepareExpressionText(sqlFunction.Arguments[i]); if (i > 0) - text = text + ", "; - text = text + paramText; + text += ", "; + text += paramText; } - text = text + ")"; + text += ")"; } return text; @@ -659,7 +661,7 @@ private static Expression UnwrapConverted(Expression expr) public MemberInfo[] GetDynamicColumns(Type type) { - return Array.Empty(); + return []; } string IMetadataReader.GetObjectID() => _objectId; diff --git a/Source/LinqToDB.EntityFrameworkCore/Internal/EFCoreExpressionAttribute.cs b/Source/LinqToDB.EntityFrameworkCore/Internal/EFCoreExpressionAttribute.cs index 2155ce44..ee794fdd 100644 --- a/Source/LinqToDB.EntityFrameworkCore/Internal/EFCoreExpressionAttribute.cs +++ b/Source/LinqToDB.EntityFrameworkCore/Internal/EFCoreExpressionAttribute.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Linq.Expressions; using LinqToDB.Mapping; @@ -10,7 +11,7 @@ namespace LinqToDB.EntityFrameworkCore.Internal /// /// Maps Linq To DB expression. /// - public class EFCoreExpressionAttribute : Sql.ExpressionAttribute + public sealed class EFCoreExpressionAttribute : Sql.ExpressionAttribute { /// /// Creates instance of expression mapper. @@ -47,7 +48,7 @@ public EFCoreExpressionAttribute(string expression) : base(expression) _ = ResolveExpressionValues((context, parms, knownExpressions, converter), Expression!, static (ctx, v, d) => { - var idx = int.Parse(v); + var idx = int.Parse(v, CultureInfo.InvariantCulture); if (ctx.parms[idx] == null) ctx.parms[idx] = ctx.converter(ctx.context, ctx.knownExpressions[idx], null); diff --git a/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.ContextExtensions.cs b/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.ContextExtensions.cs index 145feb7f..ca5d83db 100644 --- a/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.ContextExtensions.cs +++ b/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.ContextExtensions.cs @@ -121,9 +121,6 @@ public static async Task BulkCopyAsync( if (context == null) throw new ArgumentNullException(nameof(context)); if (source == null) throw new ArgumentNullException(nameof(source)); - if (context == null) throw new ArgumentNullException(nameof(context)); - if (source == null) throw new ArgumentNullException(nameof(source)); - using (var dc = context.CreateLinqToDBConnection()) { return await dc.BulkCopyAsync(maxBatchSize, source, cancellationToken).ConfigureAwait(Common.Configuration.ContinueOnCapturedContext); @@ -170,7 +167,9 @@ public static IValueInsertable Into(this DbContext context, ITable targ if (context == null) throw new ArgumentNullException(nameof(context)); if (target == null) throw new ArgumentNullException(nameof(target)); +#pragma warning disable CA2000 // Dispose objects before losing scope return context.CreateLinqToDBConnection().Into(target); +#pragma warning restore CA2000 // Dispose objects before losing scope } #endregion @@ -187,7 +186,9 @@ public static ITable GetTable(this DbContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); +#pragma warning disable CA2000 // Dispose objects before losing scope return context.CreateLinqToDBContext().GetTable(); +#pragma warning restore CA2000 // Dispose objects before losing scope } #endregion @@ -217,7 +218,7 @@ public static ITable GetTable(this DbContext context) { if (contextOptions == null) throw new ArgumentNullException(nameof(contextOptions)); - return contextOptions?.FindExtension()?.Options; + return contextOptions.FindExtension()?.Options; } #endregion diff --git a/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.Extensions.cs b/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.Extensions.cs index 7cb6f8a7..87168a4c 100644 --- a/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.Extensions.cs +++ b/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.Extensions.cs @@ -15,7 +15,9 @@ public static ITable ToLinqToDBTable(this DbSet dbSet) { var context = Implementation.GetCurrentContext(dbSet) ?? throw new LinqToDBForEFToolsException($"Can not load current context from {nameof(dbSet)}"); +#pragma warning disable CA2000 // Dispose objects before losing scope var dc = CreateLinqToDBContext(context); +#pragma warning restore CA2000 // Dispose objects before losing scope return dc.GetTable(); } diff --git a/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.Mapping.cs b/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.Mapping.cs index 672929f4..3f1c9112 100644 --- a/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.Mapping.cs +++ b/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.Mapping.cs @@ -26,18 +26,17 @@ static void InitializeMapping() static Sql.DateParts? GetDatePart(string name) { - switch (name) + return name switch { - case "Year" : return Sql.DateParts.Year; - case "Day" : return Sql.DateParts.Day; - case "Month" : return Sql.DateParts.Month; - case "Hour" : return Sql.DateParts.Hour; - case "Minute" : return Sql.DateParts.Minute; - case "Second" : return Sql.DateParts.Second; - case "Millisecond": return Sql.DateParts.Millisecond; - } - - return null; + "Year" => (Sql.DateParts?)Sql.DateParts.Year, + "Day" => (Sql.DateParts?)Sql.DateParts.Day, + "Month" => (Sql.DateParts?)Sql.DateParts.Month, + "Hour" => (Sql.DateParts?)Sql.DateParts.Hour, + "Minute" => (Sql.DateParts?)Sql.DateParts.Minute, + "Second" => (Sql.DateParts?)Sql.DateParts.Second, + "Millisecond" => (Sql.DateParts?)Sql.DateParts.Millisecond, + _ => null, + }; } /// diff --git a/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.cs b/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.cs index 4049fad9..a922c540 100644 --- a/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.cs +++ b/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.cs @@ -64,7 +64,7 @@ static bool InitializeInternal() var newExpression = queryable.Expression; var result = (IQueryable)instantiator.MakeGenericMethod(queryable.ElementType) - .Invoke(null, new object[] { dc, newExpression }); + .Invoke(null, [dc, newExpression])!; if (prev != null) result = prev(result); @@ -268,7 +268,7 @@ public static DataConnection CreateLinqToDBConnection(this DbContext context, DataConnection? dc = null; - transaction = transaction ?? context.Database.CurrentTransaction; + transaction ??= context.Database.CurrentTransaction; var connectionInfo = GetConnectionInfo(info); var provider = GetDataProvider(options, info, connectionInfo); @@ -349,7 +349,7 @@ public static IDataContext CreateLinqToDBContext(this DbContext context, DataConnection? dc = null; - transaction = transaction ?? context.Database.CurrentTransaction; + transaction ??= context.Database.CurrentTransaction; var connectionInfo = GetConnectionInfo(info); var provider = GetDataProvider(options, info, connectionInfo); @@ -588,7 +588,9 @@ public static IQueryable ToLinqToDB(this IQueryable query) var context = Implementation.GetCurrentContext(query) ?? throw new LinqToDBForEFToolsException("Can not evaluate current context from query"); +#pragma warning disable CA2000 // Dispose objects before losing scope var dc = CreateLinqToDBContext(context); +#pragma warning restore CA2000 // Dispose objects before losing scope return new LinqToDBForEFQueryProvider(dc, query.Expression); } diff --git a/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFToolsDataConnection.cs b/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFToolsDataConnection.cs index e03413d2..60a9e862 100644 --- a/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFToolsDataConnection.cs +++ b/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFToolsDataConnection.cs @@ -225,7 +225,7 @@ object IEntityServiceInterceptor.EntityCreated(EntityCreatedEventData eventData, body = Expression.Condition(checkExpression, invalidResult, body); } - body = Expression.Block(new[] { variable }, assignExpr, body); + body = Expression.Block([variable], assignExpr, body); var lambda = Expression.Lambda>>(body, stateManagerParam, objParam); diff --git a/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFToolsImplDefault.cs b/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFToolsImplDefault.cs index e7d3fc74..9fcfd680 100644 --- a/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFToolsImplDefault.cs +++ b/Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFToolsImplDefault.cs @@ -169,79 +169,38 @@ protected virtual IDataProvider CreateLinqToDBDataProvider(EFProviderInfo provid throw new LinqToDBForEFToolsException("Can not detect data provider."); } - switch (provInfo.ProviderName) + return provInfo.ProviderName switch { - case ProviderName.SqlServer: - return CreateSqlServerProvider(SqlServerDefaultVersion, connectionInfo.ConnectionString); - case ProviderName.SqlServer2005: - return CreateSqlServerProvider(SqlServerVersion.v2005, connectionInfo.ConnectionString); - case ProviderName.SqlServer2008: - return CreateSqlServerProvider(SqlServerVersion.v2008, connectionInfo.ConnectionString); - case ProviderName.SqlServer2012: - return CreateSqlServerProvider(SqlServerVersion.v2012, connectionInfo.ConnectionString); - case ProviderName.SqlServer2014: - return CreateSqlServerProvider(SqlServerVersion.v2014, connectionInfo.ConnectionString); - case ProviderName.SqlServer2016: - return CreateSqlServerProvider(SqlServerVersion.v2016, connectionInfo.ConnectionString); - case ProviderName.SqlServer2017: - return CreateSqlServerProvider(SqlServerVersion.v2017, connectionInfo.ConnectionString); - case ProviderName.SqlServer2019: - return CreateSqlServerProvider(SqlServerVersion.v2019, connectionInfo.ConnectionString); - case ProviderName.SqlServer2022: - return CreateSqlServerProvider(SqlServerVersion.v2022, connectionInfo.ConnectionString); - - case ProviderName.MySql: - case ProviderName.MySqlConnector: - case ProviderName.MariaDB: - return MySqlTools.GetDataProvider(provInfo.ProviderName); - - case ProviderName.PostgreSQL: - return CreatePostgreSqlProvider(PostgreSqlDefaultVersion, connectionInfo.ConnectionString); - case ProviderName.PostgreSQL92: - return CreatePostgreSqlProvider(PostgreSQLVersion.v92, connectionInfo.ConnectionString); - case ProviderName.PostgreSQL93: - return CreatePostgreSqlProvider(PostgreSQLVersion.v93, connectionInfo.ConnectionString); - case ProviderName.PostgreSQL95: - return CreatePostgreSqlProvider(PostgreSQLVersion.v95, connectionInfo.ConnectionString); - case ProviderName.PostgreSQL15: - return CreatePostgreSqlProvider(PostgreSQLVersion.v15, connectionInfo.ConnectionString); - - case ProviderName.SQLite: - case ProviderName.SQLiteMS: - return SQLiteTools.GetDataProvider(provInfo.ProviderName); - - case ProviderName.Firebird: - return FirebirdTools.GetDataProvider(); - - case ProviderName.DB2: - case ProviderName.DB2LUW: - return DB2Tools.GetDataProvider(DB2Version.LUW); - case ProviderName.DB2zOS: - return DB2Tools.GetDataProvider(DB2Version.zOS); - - case ProviderName.Oracle11Native: - return OracleTools.GetDataProvider(OracleVersion.v11, OracleProvider.Native); - case ProviderName.OracleNative: - return OracleTools.GetDataProvider(OracleVersion.v12, OracleProvider.Native); - case ProviderName.Oracle11Managed: - return OracleTools.GetDataProvider(OracleVersion.v11, OracleProvider.Managed); - case ProviderName.Oracle: - case ProviderName.OracleManaged: - return OracleTools.GetDataProvider(OracleVersion.v12, OracleProvider.Managed); - case ProviderName.Oracle11Devart: - return OracleTools.GetDataProvider(OracleVersion.v11, OracleProvider.Devart); - case ProviderName.OracleDevart: - return OracleTools.GetDataProvider( OracleVersion.v12, OracleProvider.Devart); - - case ProviderName.SqlCe: - return SqlCeTools.GetDataProvider(); - + ProviderName.SqlServer => CreateSqlServerProvider(SqlServerDefaultVersion, connectionInfo.ConnectionString), + ProviderName.SqlServer2005 => CreateSqlServerProvider(SqlServerVersion.v2005, connectionInfo.ConnectionString), + ProviderName.SqlServer2008 => CreateSqlServerProvider(SqlServerVersion.v2008, connectionInfo.ConnectionString), + ProviderName.SqlServer2012 => CreateSqlServerProvider(SqlServerVersion.v2012, connectionInfo.ConnectionString), + ProviderName.SqlServer2014 => CreateSqlServerProvider(SqlServerVersion.v2014, connectionInfo.ConnectionString), + ProviderName.SqlServer2016 => CreateSqlServerProvider(SqlServerVersion.v2016, connectionInfo.ConnectionString), + ProviderName.SqlServer2017 => CreateSqlServerProvider(SqlServerVersion.v2017, connectionInfo.ConnectionString), + ProviderName.SqlServer2019 => CreateSqlServerProvider(SqlServerVersion.v2019, connectionInfo.ConnectionString), + ProviderName.SqlServer2022 => CreateSqlServerProvider(SqlServerVersion.v2022, connectionInfo.ConnectionString), + ProviderName.MySql or ProviderName.MySqlConnector or ProviderName.MariaDB => MySqlTools.GetDataProvider(provInfo.ProviderName), + ProviderName.PostgreSQL => CreatePostgreSqlProvider(PostgreSqlDefaultVersion, connectionInfo.ConnectionString), + ProviderName.PostgreSQL92 => CreatePostgreSqlProvider(PostgreSQLVersion.v92, connectionInfo.ConnectionString), + ProviderName.PostgreSQL93 => CreatePostgreSqlProvider(PostgreSQLVersion.v93, connectionInfo.ConnectionString), + ProviderName.PostgreSQL95 => CreatePostgreSqlProvider(PostgreSQLVersion.v95, connectionInfo.ConnectionString), + ProviderName.PostgreSQL15 => CreatePostgreSqlProvider(PostgreSQLVersion.v15, connectionInfo.ConnectionString), + ProviderName.SQLite or ProviderName.SQLiteMS => SQLiteTools.GetDataProvider(provInfo.ProviderName), + ProviderName.Firebird => FirebirdTools.GetDataProvider(), + ProviderName.DB2 or ProviderName.DB2LUW => DB2Tools.GetDataProvider(DB2Version.LUW), + ProviderName.DB2zOS => DB2Tools.GetDataProvider(DB2Version.zOS), + ProviderName.Oracle11Native => OracleTools.GetDataProvider(OracleVersion.v11, OracleProvider.Native), + ProviderName.OracleNative => OracleTools.GetDataProvider(OracleVersion.v12, OracleProvider.Native), + ProviderName.Oracle11Managed => OracleTools.GetDataProvider(OracleVersion.v11, OracleProvider.Managed), + ProviderName.Oracle or ProviderName.OracleManaged => OracleTools.GetDataProvider(OracleVersion.v12, OracleProvider.Managed), + ProviderName.Oracle11Devart => OracleTools.GetDataProvider(OracleVersion.v11, OracleProvider.Devart), + ProviderName.OracleDevart => OracleTools.GetDataProvider(OracleVersion.v12, OracleProvider.Devart), + ProviderName.SqlCe => SqlCeTools.GetDataProvider(), //case ProviderName.Access: // return new AccessDataProvider(); - - default: - throw new LinqToDBForEFToolsException($"Can not instantiate data provider '{provInfo.ProviderName}'."); - } + _ => throw new LinqToDBForEFToolsException($"Can not instantiate data provider '{provInfo.ProviderName}'."), + }; } /// @@ -307,29 +266,18 @@ protected virtual IDataProvider CreateLinqToDBDataProvider(EFProviderInfo provid /// Linq To DB provider settings. protected virtual LinqToDBProviderInfo? GetLinqToDBProviderInfo(DbConnection connection) { - switch (connection.GetType().Name) + return connection.GetType().Name switch { - case "SqlConnection": - return new LinqToDBProviderInfo { ProviderName = ProviderName.SqlServer }; - case "MySqlConnection": - return new LinqToDBProviderInfo { ProviderName = ProviderName.MySql }; - case "NpgsqlConnection": - case "PgSqlConnection": - return new LinqToDBProviderInfo { ProviderName = ProviderName.PostgreSQL }; - case "FbConnection": - return new LinqToDBProviderInfo { ProviderName = ProviderName.Firebird }; - case "DB2Connection": - return new LinqToDBProviderInfo { ProviderName = ProviderName.DB2LUW }; - case "OracleConnection": - return new LinqToDBProviderInfo { ProviderName = ProviderName.Oracle }; - case "SqliteConnection": - case "SQLiteConnection": - return new LinqToDBProviderInfo { ProviderName = ProviderName.SQLite }; - case "JetConnection": - return new LinqToDBProviderInfo { ProviderName = ProviderName.Access }; - } - - return null; + "SqlConnection" => new LinqToDBProviderInfo { ProviderName = ProviderName.SqlServer }, + "MySqlConnection" => new LinqToDBProviderInfo { ProviderName = ProviderName.MySql }, + "NpgsqlConnection" or "PgSqlConnection" => new LinqToDBProviderInfo { ProviderName = ProviderName.PostgreSQL }, + "FbConnection" => new LinqToDBProviderInfo { ProviderName = ProviderName.Firebird }, + "DB2Connection" => new LinqToDBProviderInfo { ProviderName = ProviderName.DB2LUW }, + "OracleConnection" => new LinqToDBProviderInfo { ProviderName = ProviderName.Oracle }, + "SqliteConnection" or "SQLiteConnection" => new LinqToDBProviderInfo { ProviderName = ProviderName.SQLite }, + "JetConnection" => new LinqToDBProviderInfo { ProviderName = ProviderName.Access }, + _ => null, + }; } /// @@ -339,33 +287,20 @@ protected virtual IDataProvider CreateLinqToDBDataProvider(EFProviderInfo provid /// Linq To DB provider settings. protected virtual LinqToDBProviderInfo? GetLinqToDBProviderInfo(RelationalOptionsExtension extensions) { - switch (extensions.GetType().Name) + return extensions.GetType().Name switch { - case "MySqlOptionsExtension": - return new LinqToDBProviderInfo { ProviderName = ProviderName.MySqlConnector }; - case "MySQLOptionsExtension": - return new LinqToDBProviderInfo { ProviderName = ProviderName.MySql }; - case "NpgsqlOptionsExtension": - case "PgSqlOptionsExtension": - return new LinqToDBProviderInfo { ProviderName = ProviderName.PostgreSQL }; - case "SqlServerOptionsExtension": - return new LinqToDBProviderInfo { ProviderName = ProviderName.SqlServer }; - case "SqliteOptionsExtension": - case "SQLiteOptionsExtension": - return new LinqToDBProviderInfo { ProviderName = ProviderName.SQLite }; - case "SqlCeOptionsExtension": - return new LinqToDBProviderInfo { ProviderName = ProviderName.SqlCe }; - case "FbOptionsExtension": - return new LinqToDBProviderInfo { ProviderName = ProviderName.Firebird }; - case "Db2OptionsExtension": - return new LinqToDBProviderInfo { ProviderName = ProviderName.DB2LUW }; - case "OracleOptionsExtension": - return new LinqToDBProviderInfo { ProviderName = ProviderName.Oracle }; - case "JetOptionsExtension": - return new LinqToDBProviderInfo { ProviderName = ProviderName.Access }; - } - - return null; + "MySqlOptionsExtension" => new LinqToDBProviderInfo { ProviderName = ProviderName.MySqlConnector }, + "MySQLOptionsExtension" => new LinqToDBProviderInfo { ProviderName = ProviderName.MySql }, + "NpgsqlOptionsExtension" or "PgSqlOptionsExtension" => new LinqToDBProviderInfo { ProviderName = ProviderName.PostgreSQL }, + "SqlServerOptionsExtension" => new LinqToDBProviderInfo { ProviderName = ProviderName.SqlServer }, + "SqliteOptionsExtension" or "SQLiteOptionsExtension" => new LinqToDBProviderInfo { ProviderName = ProviderName.SQLite }, + "SqlCeOptionsExtension" => new LinqToDBProviderInfo { ProviderName = ProviderName.SqlCe }, + "FbOptionsExtension" => new LinqToDBProviderInfo { ProviderName = ProviderName.Firebird }, + "Db2OptionsExtension" => new LinqToDBProviderInfo { ProviderName = ProviderName.DB2LUW }, + "OracleOptionsExtension" => new LinqToDBProviderInfo { ProviderName = ProviderName.Oracle }, + "JetOptionsExtension" => new LinqToDBProviderInfo { ProviderName = ProviderName.Access }, + _ => null, + }; } /// @@ -574,7 +509,7 @@ public virtual MappingSchema GetMappingSchema( static readonly MethodInfo IgnoreQueryFiltersMethodInfo = MemberHelper.MethodOfGeneric>(q => q.IgnoreQueryFilters()); - static readonly MethodInfo IncludeMethodInfo = MemberHelper.MethodOfGeneric>(q => q.Include(o => o.ToString())); + static readonly MethodInfo IncludeMethodInfo = MemberHelper.MethodOfGeneric>(q => q.Include(o => o)); static readonly MethodInfo IncludeMethodInfoString = MemberHelper.MethodOfGeneric>(q => q.Include(string.Empty)); @@ -868,9 +803,9 @@ TransformInfo LocalTransform(Expression e) var propName = (string)EvaluateExpression(methodCall.Arguments[1])!; var param = Expression.Parameter(methodCall.Method.GetGenericArguments()[0], "e"); - var propPath = propName.Split(new[] {'.'}, StringSplitOptions.RemoveEmptyEntries); + var propPath = propName.Split(_propSeparator, StringSplitOptions.RemoveEmptyEntries); var prop = (Expression)param; - for (int i = 0; i < propPath.Length; i++) + for (var i = 0; i < propPath.Length; i++) { prop = Expression.PropertyOrField(prop, propPath[i]); } @@ -1201,5 +1136,7 @@ public virtual void LogConnectionTrace(TraceInfo info, ILogger logger) /// Entities will be attached only if AsNoTracking() is not used in query and DbContext is configured to track entities. /// public virtual bool EnableChangeTracker { get; set; } = true; + + private static readonly char[] _propSeparator = ['.']; } } diff --git a/Source/LinqToDB.EntityFrameworkCore/LinqToDBProviderInfo.cs b/Source/LinqToDB.EntityFrameworkCore/LinqToDBProviderInfo.cs index f3711c7b..63a2bc43 100644 --- a/Source/LinqToDB.EntityFrameworkCore/LinqToDBProviderInfo.cs +++ b/Source/LinqToDB.EntityFrameworkCore/LinqToDBProviderInfo.cs @@ -24,8 +24,8 @@ public void Merge(LinqToDBProviderInfo? providerInfo) { if (providerInfo != null) { - Version = Version ?? providerInfo.Version; - ProviderName = ProviderName ?? providerInfo.ProviderName; + Version ??= providerInfo.Version; + ProviderName ??= providerInfo.ProviderName; } } } diff --git a/Source/LinqToDB.EntityFrameworkCore/linq2db.EntityFrameworkCore.csproj b/Source/LinqToDB.EntityFrameworkCore/linq2db.EntityFrameworkCore.csproj index 85e0bf82..55d4d50b 100644 --- a/Source/LinqToDB.EntityFrameworkCore/linq2db.EntityFrameworkCore.csproj +++ b/Source/LinqToDB.EntityFrameworkCore/linq2db.EntityFrameworkCore.csproj @@ -27,6 +27,7 @@ all runtime; build; native; contentfiles; analyzers + diff --git a/Tests/.editorconfig b/Tests/.editorconfig new file mode 100644 index 00000000..c0448b0d --- /dev/null +++ b/Tests/.editorconfig @@ -0,0 +1 @@ +[*.cs] diff --git a/Tests/Directory.Build.props b/Tests/Directory.Build.props index a7324b41..69fda22a 100644 --- a/Tests/Directory.Build.props +++ b/Tests/Directory.Build.props @@ -14,6 +14,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Tests/LinqToDB.EntityFrameworkCore.BaseTests/ForMappingTestsBase.cs b/Tests/LinqToDB.EntityFrameworkCore.BaseTests/ForMappingTestsBase.cs index bcc637d9..c987df9f 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.BaseTests/ForMappingTestsBase.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.BaseTests/ForMappingTestsBase.cs @@ -12,7 +12,7 @@ namespace LinqToDB.EntityFrameworkCore.BaseTests { public abstract class ForMappingTestsBase : TestsBase { - public abstract ForMappingContextBase CreateContext(Func? optionsSetter = null); + protected abstract ForMappingContextBase CreateContext(Func? optionsSetter = null); [Test] public virtual void TestIdentityMapping() @@ -126,10 +126,10 @@ public virtual void TestMappingSchemaCached() using var connection1 = context1.CreateLinqToDBConnection(); using var connection2 = context2.CreateLinqToDBConnection(); - Assert.AreEqual(connection1.MappingSchema, connection2.MappingSchema); + Assert.That(connection2.MappingSchema, Is.EqualTo(connection1.MappingSchema)); } - sealed class TestEntity + protected sealed class TestEntity { public int Field { get; set; } } @@ -148,7 +148,7 @@ public virtual void TestMappingSchemaCachedWithCustomSchema() using var connection1 = context1.CreateLinqToDBConnection(); using var connection2 = context2.CreateLinqToDBConnection(); - Assert.AreEqual(connection1.MappingSchema, connection2.MappingSchema); + Assert.That(connection2.MappingSchema, Is.EqualTo(connection1.MappingSchema)); // check EF mapping is in place var ed = connection1.MappingSchema.GetEntityDescriptor(typeof(WithIdentity)); @@ -159,7 +159,7 @@ public virtual void TestMappingSchemaCachedWithCustomSchema() ed = connection1.MappingSchema.GetEntityDescriptor(typeof(TestEntity)); pk = ed.Columns.Single(c => c.IsPrimaryKey); pk.IsIdentity.Should().BeFalse(); - Assert.AreEqual("Field", pk.ColumnName); + Assert.That(pk.ColumnName, Is.EqualTo("Field")); } [Test] diff --git a/Tests/LinqToDB.EntityFrameworkCore.BaseTests/Logging/LogMessageEntry.cs b/Tests/LinqToDB.EntityFrameworkCore.BaseTests/Logging/LogMessageEntry.cs index b9270545..008c2734 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.BaseTests/Logging/LogMessageEntry.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.BaseTests/Logging/LogMessageEntry.cs @@ -2,26 +2,12 @@ namespace LinqToDB.EntityFrameworkCore.BaseTests.Logging { - internal readonly struct LogMessageEntry - { - public LogMessageEntry(string message, string? timeStamp = null, string? levelString = null, ConsoleColor? levelBackground = null, ConsoleColor? levelForeground = null, ConsoleColor? messageColor = null, bool logAsError = false) - { - TimeStamp = timeStamp; - LevelString = levelString; - LevelBackground = levelBackground; - LevelForeground = levelForeground; - MessageColor = messageColor; - Message = message; - LogAsError = logAsError; - } - - public readonly string? TimeStamp; - public readonly string? LevelString; - public readonly ConsoleColor? LevelBackground; - public readonly ConsoleColor? LevelForeground; - public readonly ConsoleColor? MessageColor; - public readonly string Message; - public readonly bool LogAsError; - } - + internal readonly record struct LogMessageEntry( + string Message, + string? TimeStamp = null, + string? LevelString = null, + ConsoleColor? LevelBackground = null, + ConsoleColor? LevelForeground = null, + ConsoleColor? MessageColor = null, + bool LogAsError = false); } diff --git a/Tests/LinqToDB.EntityFrameworkCore.BaseTests/Logging/TestLogger.cs b/Tests/LinqToDB.EntityFrameworkCore.BaseTests/Logging/TestLogger.cs index 2f616d65..cd7b631c 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.BaseTests/Logging/TestLogger.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.BaseTests/Logging/TestLogger.cs @@ -8,9 +8,9 @@ namespace LinqToDB.EntityFrameworkCore.BaseTests.Logging { internal sealed class TestLogger : ILogger { - private static readonly string _loglevelPadding = ": "; - private static readonly string _messagePadding; - private static readonly string _newLineWithMessagePadding; + private const string _logLevelPadding = ": "; + private static readonly string _messagePadding = new (' ', GetLogLevelString(LogLevel.Critical).Length + _logLevelPadding.Length); + private static readonly string _newLineWithMessagePadding = Environment.NewLine + _messagePadding; // ConsoleColor does not have a value to specify the 'Default' color #pragma warning disable 649 @@ -22,13 +22,6 @@ internal sealed class TestLogger : ILogger [ThreadStatic] private static StringBuilder? _logBuilder; - static TestLogger() - { - var logLevelString = GetLogLevelString(LogLevel.Information); - _messagePadding = new string(' ', logLevelString.Length + _loglevelPadding.Length); - _newLineWithMessagePadding = Environment.NewLine + _messagePadding; - } - internal TestLogger(string name) { _name = name ?? throw new ArgumentNullException(nameof(name)); @@ -118,7 +111,7 @@ private LogMessageEntry CreateDefaultLogMessage(StringBuilder logBuilder, LogLev var logLevelColors = GetLogLevelConsoleColors(logLevel); var logLevelString = GetLogLevelString(logLevel); // category and event id - logBuilder.Append(_loglevelPadding) + logBuilder.Append(_logLevelPadding) .Append(logName) .Append('[') .Append(eventId) @@ -151,13 +144,13 @@ private LogMessageEntry CreateDefaultLogMessage(StringBuilder logBuilder, LogLev #pragma warning restore CS0618 // Type or member is obsolete return new LogMessageEntry( - message: logBuilder.ToString(), - timeStamp: timestampFormat != null ? DateTime.Now.ToString(timestampFormat) : null, - levelString: logLevelString, - levelBackground: logLevelColors.Background, - levelForeground: logLevelColors.Foreground, - messageColor: DefaultConsoleColor, - logAsError: logLevel >= Options!.LogToStandardErrorThreshold + Message: logBuilder.ToString(), + TimeStamp: timestampFormat != null ? FormattableString.Invariant($"{DateTime.Now:timestampFormat}") : null, + LevelString: logLevelString, + LevelBackground: logLevelColors.Background, + LevelForeground: logLevelColors.Foreground, + MessageColor: DefaultConsoleColor, + LogAsError: logLevel >= Options!.LogToStandardErrorThreshold ); } @@ -179,7 +172,7 @@ private LogMessageEntry CreateSystemdLogMessage(StringBuilder logBuilder, LogLev #pragma warning restore CS0618 // Type or member is obsolete if (timestampFormat != null) { - logBuilder.Append(DateTime.Now.ToString(timestampFormat)); + logBuilder.Append(FormattableString.Invariant($"{DateTime.Now:timestampFormat}")); } // category and event id @@ -211,8 +204,8 @@ private LogMessageEntry CreateSystemdLogMessage(StringBuilder logBuilder, LogLev logBuilder.Append(Environment.NewLine); return new LogMessageEntry( - message: logBuilder.ToString(), - logAsError: logLevel >= Options.LogToStandardErrorThreshold + Message: logBuilder.ToString(), + LogAsError: logLevel >= Options.LogToStandardErrorThreshold ); static void AppendAndReplaceNewLine(StringBuilder sb, string message) @@ -232,44 +225,30 @@ public bool IsEnabled(LogLevel logLevel) private static string GetLogLevelString(LogLevel logLevel) { - switch (logLevel) + return logLevel switch { - case LogLevel.Trace: - return "trce"; - case LogLevel.Debug: - return "dbug"; - case LogLevel.Information: - return "info"; - case LogLevel.Warning: - return "warn"; - case LogLevel.Error: - return "fail"; - case LogLevel.Critical: - return "crit"; - default: - throw new ArgumentOutOfRangeException(nameof(logLevel)); - } + LogLevel.Trace => "trce", + LogLevel.Debug => "dbug", + LogLevel.Information => "info", + LogLevel.Warning => "warn", + LogLevel.Error => "fail", + LogLevel.Critical => "crit", + _ => throw new ArgumentOutOfRangeException(nameof(logLevel)), + }; } private static string GetSyslogSeverityString(LogLevel logLevel) { // 'Syslog Message Severities' from https://tools.ietf.org/html/rfc5424. - switch (logLevel) + return logLevel switch { - case LogLevel.Trace: - case LogLevel.Debug: - return "<7>"; // debug-level messages - case LogLevel.Information: - return "<6>"; // informational messages - case LogLevel.Warning: - return "<4>"; // warning conditions - case LogLevel.Error: - return "<3>"; // error conditions - case LogLevel.Critical: - return "<2>"; // critical conditions - default: - throw new ArgumentOutOfRangeException(nameof(logLevel)); - } + LogLevel.Trace or LogLevel.Debug => "<7>",// debug-level messages + LogLevel.Information => "<6>",// informational messages + LogLevel.Warning => "<4>",// warning conditions + LogLevel.Error => "<3>",// error conditions + LogLevel.Critical => "<2>",// critical conditions + _ => throw new ArgumentOutOfRangeException(nameof(logLevel)), + }; } private ConsoleColors GetLogLevelConsoleColors(LogLevel logLevel) @@ -283,23 +262,16 @@ private ConsoleColors GetLogLevelConsoleColors(LogLevel logLevel) // We must explicitly set the background color if we are setting the foreground color, // since just setting one can look bad on the users console. - switch (logLevel) + return logLevel switch { - case LogLevel.Critical: - return new ConsoleColors(ConsoleColor.White, ConsoleColor.Red); - case LogLevel.Error: - return new ConsoleColors(ConsoleColor.Black, ConsoleColor.Red); - case LogLevel.Warning: - return new ConsoleColors(ConsoleColor.Yellow, ConsoleColor.Black); - case LogLevel.Information: - return new ConsoleColors(ConsoleColor.DarkGreen, ConsoleColor.Black); - case LogLevel.Debug: - return new ConsoleColors(ConsoleColor.Gray, ConsoleColor.Black); - case LogLevel.Trace: - return new ConsoleColors(ConsoleColor.Gray, ConsoleColor.Black); - default: - return new ConsoleColors(DefaultConsoleColor, DefaultConsoleColor); - } + LogLevel.Critical => new ConsoleColors(ConsoleColor.White, ConsoleColor.Red), + LogLevel.Error => new ConsoleColors(ConsoleColor.Black, ConsoleColor.Red), + LogLevel.Warning => new ConsoleColors(ConsoleColor.Yellow, ConsoleColor.Black), + LogLevel.Information => new ConsoleColors(ConsoleColor.DarkGreen, ConsoleColor.Black), + LogLevel.Debug => new ConsoleColors(ConsoleColor.Gray, ConsoleColor.Black), + LogLevel.Trace => new ConsoleColors(ConsoleColor.Gray, ConsoleColor.Black), + _ => new ConsoleColors(DefaultConsoleColor, DefaultConsoleColor), + }; } private void GetScopeInformation(StringBuilder stringBuilder, bool multiLine) @@ -334,18 +306,7 @@ private void GetScopeInformation(StringBuilder stringBuilder, bool multiLine) } } - private readonly struct ConsoleColors - { - public ConsoleColors(ConsoleColor? foreground, ConsoleColor? background) - { - Foreground = foreground; - Background = background; - } - - public ConsoleColor? Foreground { get; } - - public ConsoleColor? Background { get; } - } + private readonly record struct ConsoleColors(ConsoleColor? Foreground, ConsoleColor? Background); } } diff --git a/Tests/LinqToDB.EntityFrameworkCore.BaseTests/Logging/TestLoggerProvider.cs b/Tests/LinqToDB.EntityFrameworkCore.BaseTests/Logging/TestLoggerProvider.cs index ce4bc2fe..e40dac11 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.BaseTests/Logging/TestLoggerProvider.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.BaseTests/Logging/TestLoggerProvider.cs @@ -10,12 +10,12 @@ namespace LinqToDB.EntityFrameworkCore.BaseTests.Logging /// A provider of instances. /// [ProviderAlias("Console")] - public class TestLoggerProvider : ILoggerProvider, ISupportExternalScope + public sealed class TestLoggerProvider : ILoggerProvider, ISupportExternalScope { private readonly IOptionsMonitor _options; private readonly ConcurrentDictionary _loggers; - private readonly IDisposable _optionsReloadToken; + private readonly IDisposable? _optionsReloadToken; private IExternalScopeProvider _scopeProvider = NullExternalScopeProvider.Instance; /// diff --git a/Tests/LinqToDB.EntityFrameworkCore.BaseTests/Models/Northwind/NorthwindData.Objects.cs b/Tests/LinqToDB.EntityFrameworkCore.BaseTests/Models/Northwind/NorthwindData.Objects.cs index 515aa108..6ebdd0be 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.BaseTests/Models/Northwind/NorthwindData.Objects.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.BaseTests/Models/Northwind/NorthwindData.Objects.cs @@ -10,9 +10,9 @@ public partial class NorthwindData #region Customers public static Customer[] CreateCustomers() - => new[] - { - new Customer + => + [ + new Customer { CustomerId = "ALFKI", CompanyName = "Alfreds Futterkiste", @@ -1286,16 +1286,16 @@ public static Customer[] CreateCustomers() Phone = "(26) 642-7012", Fax = "(26) 642-7012" } - }; + ]; #endregion #region Employees public static Employee[] CreateEmployees() - => new[] - { - new Employee + => + [ + new Employee { EmployeeId = 1, LastName = "Davolio", @@ -1513,16 +1513,16 @@ public static Employee[] CreateEmployees() ReportsTo = 5, PhotoPath = "http://accweb/emmployees/davolio.bmp" } - }; + ]; #endregion #region Shippers public static Shipper[] CreateShippers() - => new[] - { - new Shipper + => + [ + new Shipper { ShipperId = 1, CompanyName = "Speedy Express", @@ -1540,16 +1540,16 @@ public static Shipper[] CreateShippers() CompanyName = "Federal Shipping", Phone = "(503) 555-9931" } - }; + ]; #endregion #region Products public static Supplier[] CreateSupliers() - => new[] - { - new Supplier + => + [ + new Supplier { SupplierId = 1, CompanyName = "Exotic Liquids", @@ -1987,12 +1987,12 @@ public static Supplier[] CreateSupliers() Fax = "(514) 555-2921", HomePage = null } - }; + ]; public static Category[] CreateCategories() - => new[] - { - new Category + => + [ + new Category { CategoryId = 1, CategoryName = "Beverages", @@ -2040,12 +2040,12 @@ public static Category[] CreateCategories() CategoryName = "Seafood", Description = "Seaweed and fish" }, - }; + ]; public static Product[] CreateProducts() - => new[] - { - new Product + => + [ + new Product { ProductId = 1, ProductName = "Chai", @@ -3047,16 +3047,16 @@ public static Product[] CreateProducts() ReorderLevel = 15, Discontinued = false } - }; + ]; #endregion #region Orders public static Order[] CreateOrders() - => new[] - { - new Order + => + [ + new Order { OrderId = 10248, CustomerId = "VINET", @@ -17166,16 +17166,16 @@ public static Order[] CreateOrders() ShipPostalCode = "87110", ShipCountry = "USA" } - }; + ]; #endregion #region OrderDetails public static OrderDetail[] CreateOrderDetails() - => new[] - { - new OrderDetail + => + [ + new OrderDetail { OrderId = 10248, ProductId = 11, @@ -34415,7 +34415,7 @@ public static OrderDetail[] CreateOrderDetails() Quantity = 2, Discount = 0f } - }; + ]; #endregion } diff --git a/Tests/LinqToDB.EntityFrameworkCore.BaseTests/TestUtils.cs b/Tests/LinqToDB.EntityFrameworkCore.BaseTests/TestUtils.cs index f7ea7191..9aa97ab3 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.BaseTests/TestUtils.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.BaseTests/TestUtils.cs @@ -4,7 +4,7 @@ namespace LinqToDB.EntityFrameworkCore.BaseTests { - public class TestUtils + public static class TestUtils { public static readonly ILoggerFactory LoggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(builder => diff --git a/Tests/LinqToDB.EntityFrameworkCore.BaseTests/TestsBase.cs b/Tests/LinqToDB.EntityFrameworkCore.BaseTests/TestsBase.cs index b202b93c..0334fd19 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.BaseTests/TestsBase.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.BaseTests/TestsBase.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.Linq; using System.Text; using LinqToDB.Reflection; @@ -70,8 +71,8 @@ protected void AreEqual( } if (!allowEmpty) - Assert.AreNotEqual(0, expectedList.Count, "Expected list cannot be empty."); - Assert.AreEqual(expectedList.Count, resultList.Count, "Expected and result lists are different. Length: "); + Assert.That(expectedList, Is.Not.Empty, "Expected list cannot be empty."); + Assert.That(resultList, Has.Count.EqualTo(expectedList.Count), "Expected and result lists are different. Length: "); var exceptExpectedList = resultList.Except(expectedList, comparer).ToList(); var exceptResultList = expectedList.Except(resultList, comparer).ToList(); @@ -88,13 +89,16 @@ protected void AreEqual( for (var i = 0; i < resultList.Count; i++) { Debug.WriteLine("{0} {1} --- {2}", comparer.Equals(expectedList[i], resultList[i]) ? " " : "-", expectedList[i], resultList[i]); - message.AppendFormat("{0} {1} --- {2}", comparer.Equals(expectedList[i], resultList[i]) ? " " : "-", expectedList[i], resultList[i]); + message.AppendFormat(CultureInfo.InvariantCulture, "{0} {1} --- {2}", comparer.Equals(expectedList[i], resultList[i]) ? " " : "-", expectedList[i], resultList[i]); message.AppendLine(); } } - Assert.AreEqual(0, exceptExpected, $"Expected Was{Environment.NewLine}{message}"); - Assert.AreEqual(0, exceptResult, $"Expect Result{Environment.NewLine}{message}"); + Assert.Multiple(() => + { + Assert.That(exceptExpected, Is.EqualTo(0), $"Expected Was{Environment.NewLine}{message}"); + Assert.That(exceptResult, Is.EqualTo(0), $"Expect Result{Environment.NewLine}{message}"); + }); } protected void AreEqual(IEnumerable> expected, IEnumerable> result) @@ -102,8 +106,11 @@ protected void AreEqual(IEnumerable> expected, IEnumerable + { + Assert.That(expectedList, Is.Not.Empty); + Assert.That(resultList, Has.Count.EqualTo(expectedList.Count), "Expected and result lists are different. Length: "); + }); for (var i = 0; i < resultList.Count; i++) { @@ -120,8 +127,11 @@ protected void AreSame(IEnumerable expected, IEnumerable result) var resultList = result.ToList(); var expectedList = expected.ToList(); - Assert.AreNotEqual(0, expectedList.Count); - Assert.AreEqual(expectedList.Count, resultList.Count); + Assert.Multiple(() => + { + Assert.That(expectedList, Is.Not.Empty); + Assert.That(resultList, Has.Count.EqualTo(expectedList.Count)); + }); var b = expectedList.SequenceEqual(resultList); @@ -129,7 +139,7 @@ protected void AreSame(IEnumerable expected, IEnumerable result) for (var i = 0; i < resultList.Count; i++) Debug.WriteLine("{0} {1} --- {2}", Equals(expectedList[i], resultList[i]) ? " " : "-", expectedList[i], resultList[i]); - Assert.IsTrue(b); + Assert.That(b, Is.True); } } diff --git a/Tests/LinqToDB.EntityFrameworkCore.PomeloMySql.Tests/ForMappingTests.cs b/Tests/LinqToDB.EntityFrameworkCore.PomeloMySql.Tests/ForMappingTests.cs index 46b72818..061775d0 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.PomeloMySql.Tests/ForMappingTests.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.PomeloMySql.Tests/ForMappingTests.cs @@ -10,7 +10,7 @@ public class ForMappingTests : ForMappingTestsBase { private bool _isDbCreated; - public override ForMappingContextBase CreateContext(Func? optionsSetter = null) + protected override ForMappingContextBase CreateContext(Func? optionsSetter = null) { var optionsBuilder = new DbContextOptionsBuilder(); //var connectionString = "Server=DBHost;Port=3306;Database=TestData;Uid=TestUser;Pwd=TestPassword;charset=utf8;"; diff --git a/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/ForMappingTests.cs b/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/ForMappingTests.cs index 995ac90f..a633c055 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/ForMappingTests.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/ForMappingTests.cs @@ -12,7 +12,7 @@ public class ForMappingTests : ForMappingTestsBase { private bool _isDbCreated; - public override ForMappingContextBase CreateContext(Func? optionsSetter = null) + protected override ForMappingContextBase CreateContext(Func? optionsSetter = null) { var optionsBuilder = new DbContextOptionsBuilder(); //optionsBuilder.UseNpgsql("Server=DBHost;Port=5432;Database=ForMapping;User Id=postgres;Password=TestPassword;Pooling=true;MinPoolSize=10;MaxPoolSize=100;"); diff --git a/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/NpgSqlTests.cs b/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/NpgSqlTests.cs index 968596bd..14efcf51 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/NpgSqlTests.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/NpgSqlTests.cs @@ -95,7 +95,7 @@ public void TestConcurrencyToken() using var db = CreateNpgSqlEntitiesContext(); var toInsert = Enumerable.Range(1, 10) - .Select(i => new EntityWithXmin { Value = "Str" + i }) + .Select(i => new EntityWithXmin { Value = FormattableString.Invariant($"Str{i}") }) .ToArray(); db.BulkCopy(toInsert); diff --git a/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/SampleTests/AAA.cs b/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/SampleTests/AAA.cs index cff4b48e..2ea061f3 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/SampleTests/AAA.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/SampleTests/AAA.cs @@ -1,40 +1,37 @@ using System; using System.Threading.Tasks; -#pragma warning disable 8604 -#pragma warning disable CS8625 - namespace LinqToDB.EntityFrameworkCore.PostgreSQL.Tests.SampleTests { public class Unit { - } - + public static class ExceptionExtensions { public static Unit Throw(this Exception e) => throw e; } - + public static class AAA { - public static ArrangeResult Arrange(this T @object, Action action) + internal static ArrangeResult Arrange(this T @object, Action action) { action(@object); return new ArrangeResult(@object, default); } - public static ArrangeResult Arrange(T @object) + internal static ArrangeResult Arrange(T @object) => new(@object, default); - public static ArrangeResult Arrange(this TMock mock, Func @object) - where TMock: notnull + internal static ArrangeResult Arrange(this TMock mock, Func @object) + where TMock : notnull => new(@object(mock), mock); - public static ActResult Act(this ArrangeResult arrange, Action act) + internal static ActResult Act(this ArrangeResult arrange, Action act) where T : notnull where TMock : notnull { +#pragma warning disable CA1031 // Do not catch general exception types try { act(arrange.Object); @@ -44,12 +41,14 @@ public static ActResult Act(this ArrangeResult arr { return new ActResult(arrange.Object, arrange.Mock, e); } +#pragma warning restore CA1031 // Do not catch general exception types } - public static ActResult Act(this ArrangeResult arrange, Func act) + internal static ActResult Act(this ArrangeResult arrange, Func act) where TResult : notnull where TMock : notnull { +#pragma warning disable CA1031 // Do not catch general exception types try { return new ActResult(act(arrange.Object), arrange.Mock, default); @@ -58,9 +57,10 @@ public static ActResult Act(this ArrangeResul { return new ActResult(default, arrange.Mock, e); } +#pragma warning restore CA1031 // Do not catch general exception types } - public static void Assert(this ActResult act, Action assert) + internal static void Assert(this ActResult act, Action assert) where T : notnull where TMock : notnull { @@ -68,7 +68,7 @@ public static void Assert(this ActResult act, Action asse assert(act.Object); } - public static void Assert(this ActResult act, Action assert) + internal static void Assert(this ActResult act, Action assert) where T : notnull where TMock : notnull { @@ -76,14 +76,15 @@ public static void Assert(this ActResult act, Action> ArrangeAsync(T @object) + internal static Task> ArrangeAsync(T @object) => Task.FromResult(new ArrangeResult(@object, default)); - public static async Task> Act(this Task> arrange, Func> act) + internal static async Task> Act(this Task> arrange, Func> act) where TMock : notnull where TResult : notnull { var a = await arrange; +#pragma warning disable CA1031 // Do not catch general exception types try { return new ActResult(await act(a.Object), a.Mock, default); @@ -92,9 +93,10 @@ public static async Task> Act(this { return new ActResult(default, a.Mock, e); } +#pragma warning restore CA1031 // Do not catch general exception types } - public static async Task Assert(this Task> act, Func assert) + internal static async Task Assert(this Task> act, Func assert) where T : notnull where TMock : notnull { @@ -102,22 +104,10 @@ public static async Task Assert(this Task> act, Fu await assert(result.Object); } - public readonly struct ArrangeResult - where TMock : notnull - { - internal ArrangeResult(T @object, TMock mock) => (Object, Mock) = (@object, mock); - internal T Object { get; } - internal TMock Mock { get; } - } + internal readonly record struct ArrangeResult(T Object, TMock? Mock) + where TMock : notnull; - public readonly struct ActResult - where T: notnull - { - internal ActResult(T @object, TMock mock, Exception? exception) - => (Object, Mock, Exception) = (@object, mock, exception); - internal T Object { get; } - internal TMock Mock { get; } - internal Exception? Exception { get; } - } + internal readonly record struct ActResult(T? Object, TMock? Mock, Exception? Exception) + where T : notnull; } } diff --git a/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/SampleTests/Id.cs b/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/SampleTests/Id.cs index a4b60529..c8fc4580 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/SampleTests/Id.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/SampleTests/Id.cs @@ -1,28 +1,29 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace LinqToDB.EntityFrameworkCore.PostgreSQL.Tests.SampleTests { public static class Id { public static Id AsId(this long id) where T : IHasId => id.AsId(); - + public static Id AsId(this TId id) where T : IHasId where TId : notnull => new(id); - } - - public readonly struct Id + } + + public readonly struct Id : IEquatable> where T : IHasId where TId : notnull { internal Id(TId value) => Value = value; TId Value { get; } - public static implicit operator TId (in Id id) => id.Value; - public static bool operator == (Id left, Id right) + public static implicit operator TId(in Id id) => id.Value; + public static bool operator ==(Id left, Id right) => EqualityComparer.Default.Equals(left.Value, right.Value); - public static bool operator != (Id left, Id right) => !(left == right); + public static bool operator !=(Id left, Id right) => !(left == right); public override string ToString() => $"{typeof(T).Name}({Value})"; public bool Equals(Id other) => EqualityComparer.Default.Equals(Value, other.Value); diff --git a/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/SampleTests/IdTests.cs b/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/SampleTests/IdTests.cs index 86d1cbb6..d6f3a30f 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/SampleTests/IdTests.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/SampleTests/IdTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using System.Linq; using FluentAssertions; @@ -38,27 +38,27 @@ IDataContext CreateLinqToDBContext(TestContext testContext) [Test] [Ignore("Incomplete.")] - public void TestInsertWithoutTracker([Values("test insert")] string name) + public void TestInsertWithoutTracker([Values("test insert")] string name) => _efContext .Arrange(CreateLinqToDBContext) .Act(c => c.Insert(new Entity { Name = name })) - .Assert(id => _efContext.Entitites.Single(e => e.Id == id).Name.Should().Be(name)); + .Assert(id => _efContext.Entities.Single(e => e.Id == id).Name.Should().Be(name)); [Test] [Ignore("Incomplete.")] - public void TestInsertWithoutNew([Values("test insert")] string name) - => _efContext.Entitites + public void TestInsertWithoutNew([Values("test insert")] string name) + => _efContext.Entities .Arrange(e => e.ToLinqToDBTable()) - .Act(e => e.InsertWithInt64Identity(() => new Entity {Name = name})) - .Assert(id => _efContext.Entitites.Single(e => e.Id == id).Name.Should().Be(name)); + .Act(e => e.InsertWithInt64Identity(() => new Entity { Name = name })) + .Assert(id => _efContext.Entities.Single(e => e.Id == id).Name.Should().Be(name)); [Test] [Ignore("Incomplete.")] - public void TestInsertEfCore([Values("test insert ef")] string name) + public void TestInsertEfCore([Values("test insert ef")] string name) => _efContext - .Arrange(c => c.Entitites.Add(new Entity {Name = "test insert ef"})) + .Arrange(c => c.Entities.Add(new Entity { Name = "test insert ef" })) .Act(_ => _efContext.SaveChanges()) - .Assert(_ => _efContext.Entitites.Single().Name.Should().Be(name)); + .Assert(_ => _efContext.Entities.Single().Name.Should().Be(name)); [Test] [Ignore("Incomplete.")] @@ -66,7 +66,7 @@ public void TestIncludeDetails([Values] bool l2db, [Values] bool tracking) => _efContext .Arrange(c => InsertDefaults(CreateLinqToDBContext(c))) .Act(c => c - .Entitites + .Entities .Where(e => e.Name == "Alpha") .Include(e => e.Details) .ThenInclude(d => d.Details) @@ -82,7 +82,7 @@ public void TestManyToManyIncludeTrackerPoison([Values] bool l2db) .Arrange(c => InsertDefaults(CreateLinqToDBContext(c))) .Act(c => { - var q = c.Entitites + var q = c.Entities .Include(e => e.Items) .ThenInclude(x => x.Item); var f = q.AsLinqToDB(l2db).AsTracking().ToArray(); @@ -90,14 +90,14 @@ public void TestManyToManyIncludeTrackerPoison([Values] bool l2db) return (First: f, Second: s); }) .Assert(r => r.First[0].Items.Count().Should().Be(r.Second[0].Items.Count())); - - + + [Test] [Ignore("Incomplete.")] public void TestManyToManyInclude([Values] bool l2db, [Values] bool tracking) => _efContext .Arrange(c => InsertDefaults(CreateLinqToDBContext(c))) - .Act(c => c.Entitites + .Act(c => c.Entities .Include(e => e.Items) .ThenInclude(x => x.Item) .AsLinqToDB(l2db) @@ -140,25 +140,25 @@ void InsertDefaults(IDataContext dataContext) var g = dataContext.Insert(new Item {Name = "Green"}); var w = dataContext.Insert(new Item {Name = "White"}); - dataContext.Insert(new Detail {Name = "Second", MasterId = a}); - dataContext.Insert(new SubDetail {Name = "Plus", MasterId = d}); - dataContext.Insert(new SubDetail {Name = "Minus", MasterId = d}); - dataContext.Insert(new Child {Name = "One", ParentId = a}); - dataContext.Insert(new Child {Name = "Two", ParentId = a}); - dataContext.Insert(new Child {Name = "Three", ParentId = a}); - dataContext.Insert(new Entity2Item {EntityId = a, ItemId = r}); - dataContext.Insert(new Entity2Item {EntityId = a, ItemId = g}); - dataContext.Insert(new Entity2Item {EntityId = b, ItemId = r}); - dataContext.Insert(new Entity2Item {EntityId = b, ItemId = w}); + dataContext.Insert(new Detail { Name = "Second", MasterId = a }); + dataContext.Insert(new SubDetail { Name = "Plus", MasterId = d }); + dataContext.Insert(new SubDetail { Name = "Minus", MasterId = d }); + dataContext.Insert(new Child { Name = "One", ParentId = a }); + dataContext.Insert(new Child { Name = "Two", ParentId = a }); + dataContext.Insert(new Child { Name = "Three", ParentId = a }); + dataContext.Insert(new Entity2Item { EntityId = a, ItemId = r }); + dataContext.Insert(new Entity2Item { EntityId = a, ItemId = g }); + dataContext.Insert(new Entity2Item { EntityId = b, ItemId = r }); + dataContext.Insert(new Entity2Item { EntityId = b, ItemId = w }); } - public class TestContext : DbContext + private sealed class TestContext : DbContext { public TestContext(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); - modelBuilder.Entity().HasKey(x => new { x.EntityId, x.ItemId}); + modelBuilder.Entity().HasKey(x => new { x.EntityId, x.ItemId }); modelBuilder .UseSnakeCase() .UseIdAsKey() @@ -166,7 +166,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) } - public DbSet Entitites { get; set; } = null!; + public DbSet Entities { get; set; } = null!; public DbSet Details { get; set; } = null!; public DbSet SubDetails { get; set; } = null!; public DbSet Items { get; set; } = null!; diff --git a/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/SampleTests/StringExtensions.cs b/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/SampleTests/StringExtensions.cs index e93bc216..0db8f6e7 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/SampleTests/StringExtensions.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/SampleTests/StringExtensions.cs @@ -10,7 +10,9 @@ public static string ToSnakeCase(this string input) return input; var startUnderscores = Regex.Match(input, @"^_+"); - return startUnderscores + Regex.Replace(input, @"([a-z0-9])([A-Z])", "$1_$2").ToLowerInvariant(); - } +#pragma warning disable CA1308 // Normalize strings to uppercase + return startUnderscores + Regex.Replace(input, @"([a-z0-9])([A-Z])", "$1_$2").ToLowerInvariant(); +#pragma warning restore CA1308 // Normalize strings to uppercase + } } } diff --git a/Tests/LinqToDB.EntityFrameworkCore.SQLite.Tests/ForMappingTests.cs b/Tests/LinqToDB.EntityFrameworkCore.SQLite.Tests/ForMappingTests.cs index e209dff4..ea8b7ffa 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.SQLite.Tests/ForMappingTests.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.SQLite.Tests/ForMappingTests.cs @@ -10,7 +10,7 @@ namespace LinqToDB.EntityFrameworkCore.SQLite.Tests [TestFixture] public class ForMappingTests : ForMappingTestsBase { - public override ForMappingContextBase CreateContext(Func? optionsSetter = null) + protected override ForMappingContextBase CreateContext(Func? optionsSetter = null) { var optionsBuilder = new DbContextOptionsBuilder(); optionsBuilder.UseSqlite("DataSource=:memory:"); diff --git a/Tests/LinqToDB.EntityFrameworkCore.SQLite.Tests/InterceptorTests.cs b/Tests/LinqToDB.EntityFrameworkCore.SQLite.Tests/InterceptorTests.cs index dcec3a36..f1a0d61e 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.SQLite.Tests/InterceptorTests.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.SQLite.Tests/InterceptorTests.cs @@ -16,25 +16,21 @@ public class InterceptorTests { private const string SQLITE_CONNECTION_STRING = "DataSource=NorthwindInMemory;Mode=Memory;Cache=Shared"; private readonly DbContextOptions _northwindOptions; + private readonly DbContextOptions _northwindOptionsWithEfCoreInterceptorsOnly; private DbConnection? _dbConnection; - static TestCommandInterceptor _testCommandInterceptor; - static TestDataContextInterceptor _testDataContextInterceptor; - static TestConnectionInterceptor _testConnectionInterceptor; - static TestEntityServiceInterceptor _testEntityServiceInterceptor; - static TestEfCoreAndLinqToDBComboInterceptor _testEfCoreAndLinqToDBInterceptor; + static TestCommandInterceptor _testCommandInterceptor = new(); + static TestDataContextInterceptor _testDataContextInterceptor = new(); + static TestConnectionInterceptor _testConnectionInterceptor = new(); + static TestEntityServiceInterceptor _testEntityServiceInterceptor = new(); + static TestEfCoreAndLinqToDBComboInterceptor _testEfCoreAndLinqToDBInterceptor = new(); static InterceptorTests() { - _testCommandInterceptor = new TestCommandInterceptor(); - _testDataContextInterceptor = new TestDataContextInterceptor(); - _testConnectionInterceptor = new TestConnectionInterceptor(); - _testEntityServiceInterceptor = new TestEntityServiceInterceptor(); - _testEfCoreAndLinqToDBInterceptor = new TestEfCoreAndLinqToDBComboInterceptor(); LinqToDBForEFTools.Initialize(); DataConnection.TurnTraceSwitchOn(); } - static DbContextOptions CreateNorthwindOptions() + static DbContextOptions CreateNorthwindOptions() { var optionsBuilder = new DbContextOptionsBuilder(); optionsBuilder.UseSqlite(SQLITE_CONNECTION_STRING); @@ -53,9 +49,19 @@ static DbContextOptions CreateNorthwindOptions() return optionsBuilder.Options; } + static DbContextOptions CreateNorthwindOptionsWithEfCoreInterceptorsOnly() + { + var optionsBuilder = new DbContextOptionsBuilder(); + optionsBuilder.UseSqlite(SQLITE_CONNECTION_STRING); + optionsBuilder.UseLoggerFactory(TestUtils.LoggerFactory); + + return optionsBuilder.Options; + } + public InterceptorTests() { _northwindOptions = CreateNorthwindOptions(); + _northwindOptionsWithEfCoreInterceptorsOnly = CreateNorthwindOptionsWithEfCoreInterceptorsOnly(); } private NorthwindContext CreateContext() @@ -64,6 +70,12 @@ private NorthwindContext CreateContext() return ctx; } + private NorthwindContext CreateContextWithoutLinqToDBExtensions() + { + var ctx = new NorthwindContext(_northwindOptionsWithEfCoreInterceptorsOnly); + return ctx; + } + [SetUp] public void Setup() { @@ -83,6 +95,16 @@ public void Setup() ((TestInterceptor)interceptor).ResetInvocations(); } } + + using var ctx2 = new NorthwindContext(_northwindOptionsWithEfCoreInterceptorsOnly); + var options2 = ctx2.GetLinqToDBOptions(); + if (options2?.DataContextOptions.Interceptors != null) + { + foreach (var interceptor in options2.DataContextOptions.Interceptors) + { + ((TestInterceptor)interceptor).ResetInvocations(); + } + } } [TearDown] @@ -103,13 +125,17 @@ orderby pd.ProductId select pd; var items = query.Take(2).ToLinqToDB().ToArray(); } - Assert.IsTrue(_testCommandInterceptor.HasInterceptorBeenInvoked); - Assert.IsTrue(_testConnectionInterceptor.HasInterceptorBeenInvoked); - Assert.IsTrue(_testEntityServiceInterceptor.HasInterceptorBeenInvoked); - //the following check is false because linq2db context is never closed together - //with the EF core context - Assert.IsFalse(_testDataContextInterceptor.HasInterceptorBeenInvoked); + Assert.Multiple(() => + { + Assert.That(_testCommandInterceptor.HasInterceptorBeenInvoked, Is.True); + Assert.That(_testConnectionInterceptor.HasInterceptorBeenInvoked, Is.True); + Assert.That(_testEntityServiceInterceptor.HasInterceptorBeenInvoked, Is.True); + + //the following check is false because linq2db context is never closed together + //with the EF core context + Assert.That(_testDataContextInterceptor.HasInterceptorBeenInvoked, Is.False); + }); } [Test] @@ -126,10 +152,14 @@ orderby pd.ProductId var items = query.Take(2).ToLinqToDB(linqToDBContext).ToArray(); var items2 = query.Take(2).ToLinqToDB(linqToDBContext).ToArray(); } - Assert.IsTrue(_testCommandInterceptor.HasInterceptorBeenInvoked); - Assert.IsTrue(_testDataContextInterceptor.HasInterceptorBeenInvoked); - Assert.IsTrue(_testConnectionInterceptor.HasInterceptorBeenInvoked); - Assert.IsTrue(_testEntityServiceInterceptor.HasInterceptorBeenInvoked); + + Assert.Multiple(() => + { + Assert.That(_testCommandInterceptor.HasInterceptorBeenInvoked, Is.True); + Assert.That(_testDataContextInterceptor.HasInterceptorBeenInvoked, Is.True); + Assert.That(_testConnectionInterceptor.HasInterceptorBeenInvoked, Is.True); + Assert.That(_testEntityServiceInterceptor.HasInterceptorBeenInvoked, Is.True); + }); } } } diff --git a/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/ForMappingTests.cs b/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/ForMappingTests.cs index 220c074c..9f6bb81d 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/ForMappingTests.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/ForMappingTests.cs @@ -15,7 +15,7 @@ public class ForMappingTests : ForMappingTestsBase { private bool _isDbCreated; - public override ForMappingContextBase CreateContext(Func? optionsSetter = null) + protected override ForMappingContextBase CreateContext(Func? optionsSetter = null) { var optionsBuilder = new DbContextOptionsBuilder(); optionsBuilder.UseSqlServer(Settings.ForMappingConnectionString); @@ -72,7 +72,7 @@ public void TestDialectUse() { using var db = CreateContext(o => o.UseSqlServer("TODO:remove after fix from linq2db (not used)", SqlServerVersion.v2005)); using var dc = db.CreateLinqToDBConnectionDetached(); - Assert.True(dc.MappingSchema.DisplayID.Contains("2005")); + Assert.That(dc.MappingSchema.DisplayID, Does.Contain("2005")); } } } diff --git a/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/JsonConverTests.cs b/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/JsonConvertTests.cs similarity index 80% rename from Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/JsonConverTests.cs rename to Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/JsonConvertTests.cs index 19c15326..f789f3fe 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/JsonConverTests.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/JsonConvertTests.cs @@ -1,142 +1,145 @@ -using System; -using System.Linq; -using LinqToDB.EntityFrameworkCore.BaseTests; -using Microsoft.EntityFrameworkCore; -using Newtonsoft.Json; -using NUnit.Framework; - -namespace LinqToDB.EntityFrameworkCore.SqlServer.Tests -{ - [TestFixture] - public class JsonConverTests : TestsBase - { - private DbContextOptions _options; - - public class LocalizedString - { - public string English { get; set; } = null!; - public string German { get; set; } = null!; - public string Slovak { get; set; } = null!; - } - - public class EventScheduleItemBase - { - public int Id { get; set; } - public virtual LocalizedString NameLocalized { get; set; } = null!; - public virtual string JsonColumn { get; set; } = null!; - } - - public enum CrashEnum : byte - { - OneValue = 0, - OtherValue = 1 - } - - public class EventScheduleItem : EventScheduleItemBase - { - public CrashEnum CrashEnum { get; set; } - public Guid GuidColumn { get; set; } - } - - public class JsonConvertContext : DbContext - { - public JsonConvertContext() - { - } - - public JsonConvertContext(DbContextOptions options) - : base(options) - { - } - - - public virtual DbSet EventScheduleItems { get; set; } = null!; - - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - if (!optionsBuilder.IsConfigured) optionsBuilder.UseSqlServer("conn string"); - } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity(entity => - { - entity.ToTable("EventScheduleItem"); - entity.Property(e => e.NameLocalized) - .HasColumnName("NameLocalized_JSON") - .HasConversion(v => JsonConvert.SerializeObject(v), - v => JsonConvert.DeserializeObject(v) ?? new()); - entity.Property(e => e.CrashEnum).HasColumnType("tinyint"); - entity.Property(e => e.GuidColumn).HasColumnType("uniqueidentifier"); - }); - } - } - - public JsonConverTests() - { - var optionsBuilder = new DbContextOptionsBuilder(); - //new SqlServerDbContextOptionsBuilder(optionsBuilder); - - optionsBuilder.UseSqlServer(Settings.JsonConvertConnectionString); - optionsBuilder.UseLoggerFactory(TestUtils.LoggerFactory); - - _options = optionsBuilder.Options; - } - - [Test] - public void TestJsonConvert() - { - LinqToDBForEFTools.Initialize(); - - // // converting from string, because usually JSON is stored as string, but it depends on DataProvider - // Mapping.MappingSchema.Default.SetConverter(v => JsonConvert.DeserializeObject(v)); - // - // // here we told linq2db how to pass converted value as DataParameter. - // Mapping.MappingSchema.Default.SetConverter(v => new DataParameter("", JsonConvert.SerializeObject(v), LinqToDB.DataType.NVarChar)); - - using (var ctx = new JsonConvertContext(_options)) - { - ctx.Database.EnsureDeleted(); - ctx.Database.EnsureCreated(); - - ctx.EventScheduleItems.Delete(); - - ctx.EventScheduleItems.Add(new EventScheduleItem() - { - NameLocalized = new LocalizedString() { English = "English", German = "German", Slovak = "Slovak" }, - GuidColumn = Guid.NewGuid() - }); - ctx.SaveChanges(); - - var queryable = ctx.EventScheduleItems - .Where(p => p.Id < 10).ToLinqToDB(); - - var items = queryable - .Select(p => new - { - p.Id, - p.NameLocalized, - p.CrashEnum, - p.GuidColumn, - }); - - var item = items.FirstOrDefault(); - - Assert.IsNotNull(item); - Assert.That(item!.NameLocalized.English, Is.EqualTo("English")); - Assert.That(item.NameLocalized.German, Is.EqualTo("German")); - Assert.That(item.NameLocalized.Slovak, Is.EqualTo("Slovak")); - - //TODO: make it work - // var concrete = queryable.Select(p => new - // { - // p.Id, - // English = p.NameLocalized.English - // }).FirstOrDefault(); - // - // Assert.That(concrete.English, Is.EqualTo("English")); - } - } - } -} +using System; +using System.Linq; +using LinqToDB.EntityFrameworkCore.BaseTests; +using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace LinqToDB.EntityFrameworkCore.SqlServer.Tests +{ + [TestFixture] + public class JsonConvertTests : TestsBase + { + private DbContextOptions _options; + + private sealed class LocalizedString + { + public string English { get; set; } = null!; + public string German { get; set; } = null!; + public string Slovak { get; set; } = null!; + } + + private class EventScheduleItemBase + { + public int Id { get; set; } + public virtual LocalizedString NameLocalized { get; set; } = null!; + public virtual string? JsonColumn { get; set; } + } + +#pragma warning disable CA1028 // Enum Storage should be Int32 + private enum CrashEnum : byte +#pragma warning restore CA1028 // Enum Storage should be Int32 + { + OneValue = 0, + OtherValue = 1 + } + + private sealed class EventScheduleItem : EventScheduleItemBase + { + public CrashEnum CrashEnum { get; set; } + public Guid GuidColumn { get; set; } + } + + private sealed class JsonConvertContext : DbContext + { + public JsonConvertContext() + { + } + + public JsonConvertContext(DbContextOptions options) + : base(options) + { + } + + public DbSet EventScheduleItems { get; set; } = null!; + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + if (!optionsBuilder.IsConfigured) optionsBuilder.UseSqlServer("conn string"); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.ToTable("EventScheduleItem"); + entity.Property(e => e.NameLocalized) + .HasColumnName("NameLocalized_JSON") + .HasConversion(v => JsonConvert.SerializeObject(v), + v => JsonConvert.DeserializeObject(v) ?? new()); + entity.Property(e => e.CrashEnum).HasColumnType("tinyint"); + entity.Property(e => e.GuidColumn).HasColumnType("uniqueidentifier"); + }); + } + } + + public JsonConvertTests() + { + var optionsBuilder = new DbContextOptionsBuilder(); + //new SqlServerDbContextOptionsBuilder(optionsBuilder); + + optionsBuilder.UseSqlServer(Settings.JsonConvertConnectionString); + optionsBuilder.UseLoggerFactory(TestUtils.LoggerFactory); + + _options = optionsBuilder.Options; + } + + [Test] + public void TestJsonConvert() + { + LinqToDBForEFTools.Initialize(); + + // // converting from string, because usually JSON is stored as string, but it depends on DataProvider + // Mapping.MappingSchema.Default.SetConverter(v => JsonConvert.DeserializeObject(v)); + // + // // here we told linq2db how to pass converted value as DataParameter. + // Mapping.MappingSchema.Default.SetConverter(v => new DataParameter("", JsonConvert.SerializeObject(v), LinqToDB.DataType.NVarChar)); + + using (var ctx = new JsonConvertContext(_options)) + { + ctx.Database.EnsureDeleted(); + ctx.Database.EnsureCreated(); + + ctx.EventScheduleItems.Delete(); + + ctx.EventScheduleItems.Add(new EventScheduleItem() + { + NameLocalized = new LocalizedString() { English = "English", German = "German", Slovak = "Slovak" }, + GuidColumn = Guid.NewGuid() + }); + ctx.SaveChanges(); + + var queryable = ctx.EventScheduleItems + .Where(p => p.Id < 10).ToLinqToDB(); + + var items = queryable + .Select(p => new + { + p.Id, + p.NameLocalized, + p.CrashEnum, + p.GuidColumn, + }); + + var item = items.FirstOrDefault(); + + Assert.That(item, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(item!.NameLocalized.English, Is.EqualTo("English")); + Assert.That(item.NameLocalized.German, Is.EqualTo("German")); + Assert.That(item.NameLocalized.Slovak, Is.EqualTo("Slovak")); + }); + + //TODO: make it work + // var concrete = queryable.Select(p => new + // { + // p.Id, + // English = p.NameLocalized.English + // }).FirstOrDefault(); + // + // Assert.That(concrete.English, Is.EqualTo("English")); + } + } + } +} diff --git a/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/Models/IssueModel/IssueContext.cs b/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/Models/IssueModel/IssueContext.cs index e8dee5c5..edab86fd 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/Models/IssueModel/IssueContext.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/Models/IssueModel/IssueContext.cs @@ -24,8 +24,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey(x => new { x.ParentId }) .HasPrincipalKey(x => new { x!.Id }); - b.HasData(new[] - { + b.HasData( + [ new Issue73Entity { Id = 2, @@ -37,7 +37,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) Name = "Name1_3", ParentId = 2 }, - }); + ]); }); modelBuilder diff --git a/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/Models/Northwind/NorthwindContext.cs b/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/Models/Northwind/NorthwindContext.cs index 52d68511..102485b4 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/Models/Northwind/NorthwindContext.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/Models/Northwind/NorthwindContext.cs @@ -64,7 +64,7 @@ private void ConfigureGlobalQueryFilters(ModelBuilder builder) if (typeof(ISoftDelete).IsSameOrParentOf(entityType.ClrType)) { var method = ConfigureEntityFilterMethodInfo.MakeGenericMethod(entityType.ClrType); - method.Invoke(this, new object[] { builder }); + method.Invoke(this, [builder]); } } } diff --git a/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/Settings.cs b/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/Settings.cs index 49834951..987dd52d 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/Settings.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/Settings.cs @@ -2,10 +2,10 @@ { public static class Settings { - public static readonly string ForMappingConnectionString = "Server=.;Database=ForMapping;Integrated Security=SSPI;Encrypt=true;TrustServerCertificate=true"; - public static readonly string IssuesConnectionString = "Server=.;Database=IssuesEFCore;Integrated Security=SSPI;Encrypt=true;TrustServerCertificate=true"; - public static readonly string JsonConvertConnectionString = "Server=.;Database=JsonConvertContext;Integrated Security=SSPI;Encrypt=true;TrustServerCertificate=true"; - public static readonly string NorthwindConnectionString = "Server=.;Database=NorthwindEFCore;Integrated Security=SSPI;Encrypt=true;TrustServerCertificate=true"; - public static readonly string ConverterConnectionString = "Server=.;Database=ConverterTests;Integrated Security=SSPI;Encrypt=true;TrustServerCertificate=true"; + public const string ForMappingConnectionString = "Server=.;Database=ForMapping;Integrated Security=SSPI;Encrypt=true;TrustServerCertificate=true"; + public const string IssuesConnectionString = "Server=.;Database=IssuesEFCore;Integrated Security=SSPI;Encrypt=true;TrustServerCertificate=true"; + public const string JsonConvertConnectionString = "Server=.;Database=JsonConvertContext;Integrated Security=SSPI;Encrypt=true;TrustServerCertificate=true"; + public const string NorthwindConnectionString = "Server=.;Database=NorthwindEFCore;Integrated Security=SSPI;Encrypt=true;TrustServerCertificate=true"; + public const string ConverterConnectionString = "Server=.;Database=ConverterTests;Integrated Security=SSPI;Encrypt=true;TrustServerCertificate=true"; } } diff --git a/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/ToolsTests.cs b/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/ToolsTests.cs index 41757dae..f6b4514e 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/ToolsTests.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/ToolsTests.cs @@ -1,5 +1,4 @@ using System; -using System.Data.SqlClient; using System.Linq; using System.Threading.Tasks; using FluentAssertions; @@ -20,7 +19,7 @@ public class ToolsTests : TestsBase { private DbContextOptions? _inheritanceOptions; private readonly DbContextOptions _options; - private readonly DbContextOptions _inmemoryOptions; + private readonly DbContextOptions _inMemoryOptions; static ToolsTests() { @@ -39,7 +38,6 @@ static DbContextOptions CreateInheritanceOptions() return optionsBuilder.Options; } - public ToolsTests() { var optionsBuilder = new DbContextOptionsBuilder(); @@ -56,12 +54,12 @@ public ToolsTests() optionsBuilder.UseInMemoryDatabase("sample"); optionsBuilder.UseLoggerFactory(TestUtils.LoggerFactory); - _inmemoryOptions = optionsBuilder.Options; + _inMemoryOptions = optionsBuilder.Options; } private NorthwindContext CreateContextInMemory() { - var ctx = new NorthwindContext(_inmemoryOptions); + var ctx = new NorthwindContext(_inMemoryOptions); ctx.Database.EnsureCreated(); return ctx; } @@ -69,16 +67,18 @@ private NorthwindContext CreateContextInMemory() private void SetIdentityInsert(DbContext ctx, string tableName, bool isOn) { var str = $"SET IDENTITY_INSERT {tableName} " + (isOn ? "ON" : "OFF"); +#pragma warning disable CA1031 // Do not catch general exception types try { #pragma warning disable EF1000 // Possible SQL injection vulnerability. ctx.Database.ExecuteSqlCommand(str); #pragma warning restore EF1000 // Possible SQL injection vulnerability. } - catch (Exception) + catch { // swallow } +#pragma warning restore CA1031 // Do not catch general exception types } private NorthwindContext CreateContext(bool enableFilter) @@ -109,14 +109,6 @@ private InheritanceContext CreateInheritanceContext() return ctx; } - public class VwProductAndDescription - { - public int ProductId { get; set; } - public string Name { get; set; } = null!; - public string ProductModel { get; set; } = null!; - public string Description { get; set; } = null!; - } - [Test] public void TestToList([Values(true, false)] bool enableFilter) { @@ -154,8 +146,10 @@ public void TestCallback([Values(true, false)] bool enableFilter) { using (var ctx = CreateContext(enableFilter)) { +#pragma warning disable CA1866 // Use char overload var query = ProductQuery(ctx) .Where(pd => pd.ProductName.StartsWith("a")); +#pragma warning restore CA1866 // Use char overload query.Where(p => p.ProductName == "a").Delete(); } @@ -167,9 +161,11 @@ public void TestContextRetrieving([Values(true, false)] bool enableFilter) { using (var ctx = CreateContext(enableFilter)) { +#pragma warning disable CA1866 // Use char overload var query = ProductQuery(ctx) .ToLinqToDB() .Where(pd => pd.ProductName.StartsWith("a")); +#pragma warning restore CA1866 // Use char overload } } @@ -178,8 +174,10 @@ public void TestDelete([Values(true, false)] bool enableFilter) { using (var ctx = CreateContext(enableFilter)) { +#pragma warning disable CA1866 // Use char overload var query = ProductQuery(ctx) .Where(pd => pd.ProductName.StartsWith("a")); +#pragma warning restore CA1866 // Use char overload } } @@ -214,30 +212,30 @@ public void TestFunctions() using (var ctx = CreateContext(false)) { var query = from p in ctx.Orders - //where EF.Functions.Like(p., "a%") || true - //orderby p.ProductId - select new - { - p.OrderId, - // Date = Model.TestFunctions.GetDate(), - // Len = Model.TestFunctions.Len(p.Name), - DiffYear1 = EF.Functions.DateDiffYear(p.ShippedDate, p.OrderDate), - DiffYear2 = p.OrderDate == null ? null : EF.Functions.DateDiffYear(p.ShippedDate, p.OrderDate.Value), - DiffMonth1 = EF.Functions.DateDiffMonth(p.ShippedDate, p.OrderDate), - DiffMonth2 = p.OrderDate == null ? null : EF.Functions.DateDiffMonth(p.ShippedDate, p.OrderDate.Value), - DiffDay1 = EF.Functions.DateDiffDay(p.ShippedDate, p.OrderDate), - DiffDay2 = p.OrderDate == null ? null : EF.Functions.DateDiffDay(p.ShippedDate, p.OrderDate.Value), - DiffHour1 = EF.Functions.DateDiffHour(p.ShippedDate, p.OrderDate), - DiffHour2 = p.OrderDate == null ? null : EF.Functions.DateDiffHour(p.ShippedDate, p.OrderDate.Value), - DiffMinute1 = EF.Functions.DateDiffMinute(p.ShippedDate, p.OrderDate), - DiffMinute2 = p.OrderDate == null ? null : EF.Functions.DateDiffMinute(p.ShippedDate, p.OrderDate.Value), - DiffSecond1 = EF.Functions.DateDiffSecond(p.ShippedDate, p.OrderDate), - DiffSecond2 = p.OrderDate == null ? null : EF.Functions.DateDiffSecond(p.ShippedDate, p.OrderDate.Value), - DiffMillisecond1 = EF.Functions.DateDiffMillisecond(p.ShippedDate, p.ShippedDate!.Value.AddMilliseconds(100)), - DiffMillisecond2 = p.OrderDate == null ? null : EF.Functions.DateDiffMillisecond(p.ShippedDate, p.ShippedDate.Value.AddMilliseconds(100)), - }; - -// var items1 = query.ToArray(); + //where EF.Functions.Like(p., "a%") || true + //orderby p.ProductId + select new + { + p.OrderId, + // Date = Model.TestFunctions.GetDate(), + // Len = Model.TestFunctions.Len(p.Name), + DiffYear1 = EF.Functions.DateDiffYear(p.ShippedDate, p.OrderDate), + DiffYear2 = p.OrderDate == null ? null : EF.Functions.DateDiffYear(p.ShippedDate, p.OrderDate.Value), + DiffMonth1 = EF.Functions.DateDiffMonth(p.ShippedDate, p.OrderDate), + DiffMonth2 = p.OrderDate == null ? null : EF.Functions.DateDiffMonth(p.ShippedDate, p.OrderDate.Value), + DiffDay1 = EF.Functions.DateDiffDay(p.ShippedDate, p.OrderDate), + DiffDay2 = p.OrderDate == null ? null : EF.Functions.DateDiffDay(p.ShippedDate, p.OrderDate.Value), + DiffHour1 = EF.Functions.DateDiffHour(p.ShippedDate, p.OrderDate), + DiffHour2 = p.OrderDate == null ? null : EF.Functions.DateDiffHour(p.ShippedDate, p.OrderDate.Value), + DiffMinute1 = EF.Functions.DateDiffMinute(p.ShippedDate, p.OrderDate), + DiffMinute2 = p.OrderDate == null ? null : EF.Functions.DateDiffMinute(p.ShippedDate, p.OrderDate.Value), + DiffSecond1 = EF.Functions.DateDiffSecond(p.ShippedDate, p.OrderDate), + DiffSecond2 = p.OrderDate == null ? null : EF.Functions.DateDiffSecond(p.ShippedDate, p.OrderDate.Value), + DiffMillisecond1 = EF.Functions.DateDiffMillisecond(p.ShippedDate, p.ShippedDate!.Value.AddMilliseconds(100)), + DiffMillisecond2 = p.OrderDate == null ? null : EF.Functions.DateDiffMillisecond(p.ShippedDate, p.ShippedDate.Value.AddMilliseconds(100)), + }; + + // var items1 = query.ToArray(); var items2 = query.ToLinqToDB().ToArray(); } } @@ -247,14 +245,16 @@ public async Task TestTransaction([Values(true, false)] bool enableFilter) { using (var ctx = CreateContext(enableFilter)) { - using (var transaction = ctx.Database.BeginTransaction()) + using (var transaction = await ctx.Database.BeginTransactionAsync()) using (var db = ctx.CreateLinqToDBConnection()) { +#pragma warning disable CA1866 // Use char overload var test1 = await ctx.Products.Where(p => p.ProductName.StartsWith("U")).MaxAsync(p => p.QuantityPerUnit); var test2 = await ctx.Products.Where(p => p.ProductName.StartsWith("U")).MaxAsyncLinqToDB(p => p.QuantityPerUnit); +#pragma warning restore CA1866 // Use char overload - Assert.AreEqual(test1, test2); + Assert.That(test2, Is.EqualTo(test1)); ctx.Products.Where(p => p.ProductName == "a") .ToLinqToDB(db) @@ -271,9 +271,11 @@ public void TestView([Values(true, false)] bool enableFilter) using (var ctx = CreateContext(enableFilter)) using (var db = ctx.CreateLinqToDBConnection()) { +#pragma warning disable CA1866 // Use char overload var query = ProductQuery(ctx) .ToLinqToDB(db) .Where(pd => pd.ProductName.StartsWith("a")); +#pragma warning restore CA1866 // Use char overload var items = query.ToArray(); } @@ -341,13 +343,16 @@ public void TestKey() using (var ctx = CreateContext(false)) { var ms = LinqToDBForEFTools.GetMappingSchema(ctx.Model, ctx, null); - + var customerPk = ms.GetAttribute(typeof(Customer), MemberHelper.MemberOf(c => c.CustomerId)); - Assert.NotNull(customerPk); - Assert.AreEqual(true, customerPk!.IsPrimaryKey); - Assert.AreEqual(0, customerPk.PrimaryKeyOrder); + Assert.That(customerPk, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(customerPk!.IsPrimaryKey, Is.EqualTo(true)); + Assert.That(customerPk.PrimaryKeyOrder, Is.EqualTo(0)); + }); } } @@ -357,13 +362,16 @@ public void TestAssociations() using (var ctx = CreateContext(false)) { var ms = LinqToDBForEFTools.GetMappingSchema(ctx.Model, ctx, null); - + var associationOrder = ms.GetAttribute(typeof(Customer), MemberHelper.MemberOf(c => c.Orders)); - Assert.NotNull(associationOrder); - Assert.That(associationOrder!.ThisKey, Is.EqualTo("CustomerId")); - Assert.That(associationOrder.OtherKey, Is.EqualTo("CustomerId")); + Assert.That(associationOrder, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(associationOrder!.ThisKey, Is.EqualTo("CustomerId")); + Assert.That(associationOrder.OtherKey, Is.EqualTo("CustomerId")); + }); } } @@ -377,11 +385,11 @@ public void TestGlobalQueryFilters([Values(true, false)] bool enableFilter) from p in ctx.Products.IgnoreQueryFilters() join d in ctx.OrderDetails on p.ProductId equals d.ProductId select new { p, d }; - + var efResult = withoutFilterQuery.ToArray(); var linq2dbResult = withoutFilterQuery.ToLinqToDB().ToArray(); - - Assert.AreEqual(efResult.Length, linq2dbResult.Length); + + Assert.That(linq2dbResult, Has.Length.EqualTo(efResult.Length)); var withFilterQuery = from p in ctx.Products @@ -391,7 +399,7 @@ join d in ctx.OrderDetails on p.ProductId equals d.ProductId var efResult2 = withFilterQuery.ToArray(); var linq2dbResult2 = withFilterQuery.ToLinqToDB().ToArray(); - Assert.AreEqual(efResult2.Length, linq2dbResult2.Length); + Assert.That(linq2dbResult2, Has.Length.EqualTo(efResult2.Length)); } } @@ -483,22 +491,22 @@ public async Task TestLoadFilter([Values(true, false)] bool enableFilter) using (var ctx = CreateContext(enableFilter)) { var query = ctx.Products.Select(p => new + { + p.ProductName, + OrderDetails = p.OrderDetails.Select(od => new { - p.ProductName, - OrderDetails = p.OrderDetails.Select(od => new - { - od.Discount, - od.Order, - od.Product.Supplier!.Products - }) - }); + od.Discount, + od.Order, + od.Product.Supplier!.Products + }) + }); ctx.IsSoftDeleteFilterEnabled = true; var expected = await query.ToArrayAsync(); var filtered = await query.ToLinqToDB().ToArrayAsync(); - Assert.That(filtered.Length, Is.EqualTo(expected.Length)); + Assert.That(filtered, Has.Length.EqualTo(expected.Length)); } } @@ -557,16 +565,16 @@ public async Task TestChangeTracker([Values(true, false)] bool enableFilter) .Include(o => o.OrderDetails) .ThenInclude(d => d.Product) .ThenInclude(p => p.OrderDetails); - + // var efResult = await query.ToArrayAsync(); var result = await query.ToLinqToDB().ToArrayAsync(); var orderDetail = result[0].OrderDetails.First(); - orderDetail.UnitPrice = orderDetail.UnitPrice * 1.1m; + orderDetail.UnitPrice *= 1.1m; ctx.ChangeTracker.DetectChanges(); var changedEntry = ctx.ChangeTracker.Entries().Single(e => e.State == EntityState.Modified); - ctx.SaveChanges(); + await ctx.SaveChangesAsync(); } } @@ -585,12 +593,12 @@ public async Task TestChangeTrackerDisabled1([Values(true, false)] bool enableFi var result = await query.ToLinqToDB().ToArrayAsync(); var orderDetail = result[0].OrderDetails.First(); - orderDetail.UnitPrice = orderDetail.UnitPrice * 1.1m; + orderDetail.UnitPrice *= 1.1m; ctx.ChangeTracker.DetectChanges(); var changedEntry = ctx.ChangeTracker.Entries().SingleOrDefault(e => e.State == EntityState.Modified); - Assert.AreEqual(changedEntry, null); - ctx.SaveChanges(); + Assert.That(changedEntry, Is.Null); + await ctx.SaveChangesAsync(); } } @@ -611,12 +619,12 @@ public async Task TestChangeTrackerDisabled2([Values(true, false)] bool enableFi var result = await query.ToLinqToDB().ToArrayAsync(); var orderDetail = result[0].OrderDetails.First(); - orderDetail.UnitPrice = orderDetail.UnitPrice * 1.1m; + orderDetail.UnitPrice *= 1.1m; ctx.ChangeTracker.DetectChanges(); var changedEntry = ctx.ChangeTracker.Entries().SingleOrDefault(e => e.State == EntityState.Modified); - Assert.AreEqual(changedEntry, null); - ctx.SaveChanges(); + Assert.That(changedEntry, Is.Null); + await ctx.SaveChangesAsync(); } } finally @@ -643,30 +651,30 @@ public async Task TestChangeTrackerTemporaryTable([Values(true, false)] bool ena //[Test] - issue in EF Core 2.x - public void NavigationProperties() - { - using (var ctx = CreateContext(false)) - { - var query = - from o in ctx.Orders - from od in o.OrderDetails - select new - { - ProductOrderDetails = - od.Product.OrderDetails.Select(d => new { d.OrderId, d.ProductId, d.Quantity }) - .OrderBy(x => x.OrderId).ThenBy(x => x.ProductId).ThenBy(x => x.Quantity).ToArray(), - OrderDetail = new { od.OrderId, od.ProductId, od.Quantity }, - Product = new { od.Product.ProductId, od.Product.ProductName } - }; - - query = query.Take(1); - - var efResult = query.ToArray(); - var l2dbResult = query.ToLinqToDB().ToArray(); - - AreEqualWithComparer(efResult, l2dbResult); - } - } + //public void NavigationProperties() + //{ + // using (var ctx = CreateContext(false)) + // { + // var query = + // from o in ctx.Orders + // from od in o.OrderDetails + // select new + // { + // ProductOrderDetails = + // od.Product.OrderDetails.Select(d => new { d.OrderId, d.ProductId, d.Quantity }) + // .OrderBy(x => x.OrderId).ThenBy(x => x.ProductId).ThenBy(x => x.Quantity).ToArray(), + // OrderDetail = new { od.OrderId, od.ProductId, od.Quantity }, + // Product = new { od.Product.ProductId, od.Product.ProductName } + // }; + + // query = query.Take(1); + + // var efResult = query.ToArray(); + // var l2dbResult = query.ToLinqToDB().ToArray(); + + // AreEqualWithComparer(efResult, l2dbResult); + // } + //} [Test] public async Task TestSetUpdate([Values(true, false)] bool enableFilter) @@ -705,7 +713,7 @@ public async Task FromSqlRaw2() var id = 1; var query = from c1 in ctx.Categories from c2 in ctx.Categories.FromSql("SELECT * FROM [dbo].[Categories] WHERE CategoryId = {0}", id) - select c2; + select c2; var efResult = await query.ToArrayAsyncEF(); var linq2dbResult = await query.ToArrayAsyncLinqToDB(); @@ -733,7 +741,7 @@ public async Task FromSqlInterpolated2() var id = 1; var query = from c1 in ctx.Categories from c2 in ctx.Categories.FromSql($"SELECT * FROM [dbo].[Categories] WHERE CategoryId = {id}") - select c2; + select c2; var efResult = await query.AsNoTracking().ToArrayAsyncEF(); var linq2dbResult = await query.AsNoTracking().ToArrayAsyncLinqToDB(); @@ -808,7 +816,7 @@ public void TestCreateTempTable([Values(true, false)] bool enableFilter) using var db = ctx.CreateLinqToDBContext(); using var temp = db.CreateTempTable(ctx.Employees, "#TestEmployees"); - Assert.AreEqual(ctx.Employees.Count(), temp.Count()); + Assert.That(temp.Count(), Is.EqualTo(ctx.Employees.Count())); } } @@ -859,12 +867,11 @@ RETURNS int var query = from p in ctx.Products select NorthwindContext.ProcessLong(commandExecutionTime); - var exception = Assert.Throws(() => + var exception = Assert.Throws(() => { var result = query.ToLinqToDB().First(); })!; - - Assert.AreEqual(exception.Number, timeoutErrorCode); + Assert.That(timeoutErrorCode, Is.EqualTo(exception.Number)); } finally { @@ -897,7 +904,7 @@ public void TestInheritanceBulkCopy([Values] BulkCopyType copyType) { var data = new BlogBase[] { new Blog() { Url = "BlogUrl" }, new RssBlog() { Url = "RssUrl" } }; - ctx.BulkCopy(new BulkCopyOptions(){ BulkCopyType = BulkCopyType.RowByRow }, data); + ctx.BulkCopy(new BulkCopyOptions() { BulkCopyType = BulkCopyType.RowByRow }, data); var items = ctx.Blogs.ToArray(); @@ -929,6 +936,5 @@ public void TestInheritanceShadowBulkCopy([Values] BulkCopyType copyType) } } */ - } } diff --git a/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/ValueConversion/ConvertorTests.cs b/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/ValueConversion/ConvertorTests.cs index 4fb95cfd..68f9dd51 100644 --- a/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/ValueConversion/ConvertorTests.cs +++ b/Tests/LinqToDB.EntityFrameworkCore.SqlServer.Tests/ValueConversion/ConvertorTests.cs @@ -13,14 +13,14 @@ public class ConvertorTests { private DbContextOptions _options; - public class ConvertorContext : DbContext + private sealed class ConvertorContext : DbContext { public ConvertorContext(DbContextOptions options) : base(options) { } [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - public virtual DbSet Subdivisions { get; set; } = null!; + public DbSet Subdivisions { get; set; } = null!; } public ConvertorTests() @@ -30,7 +30,7 @@ public ConvertorTests() optionsBuilder .ReplaceService() .UseSqlServer(Settings.ConverterConnectionString) - .UseLoggerFactory(TestUtils.LoggerFactory);; + .UseLoggerFactory(TestUtils.LoggerFactory); ; _options = optionsBuilder.Options; } @@ -46,32 +46,34 @@ public void TestToList() ctx.Database.EnsureCreated(); - var resut = db.InsertWithInt64Identity(new SubDivision() - { Code = "C1", Id = new Id(0), Name = "N1", PermanentId = Guid.NewGuid() }); + var result = db.InsertWithInt64Identity(new SubDivision() + { Code = "C1", Id = new Id(0), Name = "N1", PermanentId = Guid.NewGuid() }); - resut = db.InsertWithInt64Identity(new SubDivision() - { Code = "C2", Id = new Id(1), Name = "N2", PermanentId = Guid.NewGuid() }); + result = db.InsertWithInt64Identity(new SubDivision() + { Code = "C2", Id = new Id(1), Name = "N2", PermanentId = Guid.NewGuid() }); + + result = db.InsertWithInt64Identity(new SubDivision() + { Code = "C3", Id = new Id(2), Name = "N3", PermanentId = Guid.NewGuid() }); - resut = db.InsertWithInt64Identity(new SubDivision() - { Code = "C3", Id = new Id(2), Name = "N3", PermanentId = Guid.NewGuid() }); - var ef = ctx.Subdivisions.Where(s => s.Id == 1L).ToArray(); - var ltdb = ctx.Subdivisions.ToLinqToDB().Where(s => s.Id == 1L).ToArray(); - + var result1 = ctx.Subdivisions.ToLinqToDB().Where(s => s.Id == 1L).ToArray(); + var id = new Id?(0L.AsId()); - var ltdb2 = ctx.Subdivisions.ToLinqToDB().Where(s => s.Id == id).ToArray(); - + var result2 = ctx.Subdivisions.ToLinqToDB().Where(s => s.Id == id).ToArray(); + var ids = new[] {1L.AsId(), 2L.AsId(),}; - var ltdbin = ctx.Subdivisions.ToLinqToDB() - .Where(s => ids.Contains(s.Id)).ToArray(); - - var all = ctx.Subdivisions.ToLinqToDB().ToArray(); - - Assert.AreEqual(ef[0].Code, ltdb[0].Code); - Assert.AreEqual(ef[0].Id, ltdb[0].Id); - - Assert.AreEqual(ef[0].Code, ltdb2[0].Code); - Assert.AreEqual(ef[0].Id, ltdb2[0].Id); + _ = ctx.Subdivisions.ToLinqToDB().Where(s => ids.Contains(s.Id)).ToArray(); + + _ = ctx.Subdivisions.ToLinqToDB().ToArray(); + + Assert.Multiple(() => + { + Assert.That(result1[0].Code, Is.EqualTo(ef[0].Code)); + Assert.That(result1[0].Id, Is.EqualTo(ef[0].Id)); + + Assert.That(result2[0].Code, Is.EqualTo(ef[0].Code)); + Assert.That(result2[0].Id, Is.EqualTo(ef[0].Id)); + }); } } } diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 304991c5..c5f3ab26 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,8 +1,8 @@ variables: solution: 'linq2db.EFCore.sln' build_configuration: 'Release' - assemblyVersion: 2.13.0 - nugetVersion: 2.13.0 + assemblyVersion: 2.14.0 + nugetVersion: 2.14.0 artifact_nugets: 'nugets' # build on commits to important branches (master + release branches): diff --git a/linq2db.EFCore.sln b/linq2db.EFCore.sln index 53b64ee1..2993813d 100644 --- a/linq2db.EFCore.sln +++ b/linq2db.EFCore.sln @@ -41,6 +41,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LinqToDB.EntityFrameworkCor EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{3727B214-909A-4A19-8BF5-E281C0D7A6E5}" ProjectSection(SolutionItems) = preProject + Tests\.editorconfig = Tests\.editorconfig Tests\Directory.Build.props = Tests\Directory.Build.props EndProjectSection EndProject