From 5f18f43fc9c11b5d14f91b662e4c5646e98a7ddc Mon Sep 17 00:00:00 2001 From: Rockford lhotka Date: Sun, 13 Sep 2026 17:44:16 -0500 Subject: [PATCH 01/10] #4359 Add DataPortalExtensions and NoDataPortalExtension attributes Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Dn4aKN7kqcG78x1HBWApk3 --- Source/Csla/DataPortalExtensionsAttribute.cs | 29 +++++++++++++++++++ Source/Csla/NoDataPortalExtensionAttribute.cs | 18 ++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 Source/Csla/DataPortalExtensionsAttribute.cs create mode 100644 Source/Csla/NoDataPortalExtensionAttribute.cs diff --git a/Source/Csla/DataPortalExtensionsAttribute.cs b/Source/Csla/DataPortalExtensionsAttribute.cs new file mode 100644 index 0000000000..852d934bb6 --- /dev/null +++ b/Source/Csla/DataPortalExtensionsAttribute.cs @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// Requests generation of strongly typed data portal extension methods +//----------------------------------------------------------------------- + +namespace Csla +{ + /// + /// Requests that the CSLA source generator emit strongly typed + /// extension methods on and + /// for each data portal + /// operation method declared by the business class. + /// + /// + /// The business class must be partial and non-generic. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public sealed class DataPortalExtensionsAttribute : Attribute + { + /// + /// Gets or sets an optional prefix added to the name of + /// each generated extension method. Defaults to no prefix. + /// + public string? Prefix { get; set; } + } +} diff --git a/Source/Csla/NoDataPortalExtensionAttribute.cs b/Source/Csla/NoDataPortalExtensionAttribute.cs new file mode 100644 index 0000000000..ac26f8c1ec --- /dev/null +++ b/Source/Csla/NoDataPortalExtensionAttribute.cs @@ -0,0 +1,18 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// Excludes an operation method from data portal extension generation +//----------------------------------------------------------------------- + +namespace Csla +{ + /// + /// Excludes a data portal operation method from strongly typed + /// extension method generation requested by + /// . + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public sealed class NoDataPortalExtensionAttribute : Attribute; +} From 53ae6ea830b1ff27ba86b0b6ca70de8dca0c2cd1 Mon Sep 17 00:00:00 2001 From: Rockford lhotka Date: Sun, 13 Sep 2026 18:07:37 -0500 Subject: [PATCH 02/10] #4359 Bundle data portal operations generator into Csla with always-generated operations interface - Merge the DataPortalInterfaces generator and its tests into the AutoImplementProperties generator project; move the AutoImplement attributes into Csla core and drop the Microsoft.Bcl.HashCode dependency - Pack the generator into the Csla nupkg (analyzers/dotnet/cs) and add a buildTransitive guard against the retired standalone package - Rewrite discovery with ForAttributeWithMetadataName, grouped by type with equatable models; emit an internal nested IDataPortalOperations interface for every partial class (abstract classes included) - Fix inject parameter order, identifier sanitizing, nullable value types, inject detection, ValueTask, generic criteria, sync/async parity, keyed services, and duplicate operation names - Make Csla abstract base classes partial Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Dn4aKN7kqcG78x1HBWApk3 --- Source/Csla.Benchmarks/Csla.Benchmarks.csproj | 2 +- ...plementProperties.Attributes.CSharp.csproj | 21 - .../AutoImplement/ExtractedTypeDefinition.cs | 3 +- ...ator.AutoImplementProperties.CSharp.csproj | 25 +- .../DataPortalOperationsBuilder.cs | 339 +++++++++ .../DataPortalOperationsDiagnostics.cs | 77 ++ ...ncrementalDataPortalOperationsGenerator.cs | 98 +++ .../Models/LocationInfo.cs | 41 ++ .../Models/OperationMethodModel.cs | 66 ++ .../Models/OperationParameterModel.cs | 75 ++ .../Models/OperationTypeModel.cs | 67 ++ .../OperationDiscovery.cs | 441 ++++++++++++ .../DataPortalOperations/TrackingNames.cs | 31 + .../Internals/EquatableArray.cs | 73 ++ .../Internals/HashCode.cs | 106 +++ .../tools/install.ps1 | 49 -- .../tools/uninstall.ps1 | 56 -- ...nerator.DataPortalInterfaces.CSharp.csproj | 31 - .../DataPortalInterfaceBuilder.cs | 287 -------- .../ContainerDefinitionsExtractor.cs | 110 --- .../Discovery/DefinitionExtractionContext.cs | 52 -- .../Discovery/OperationMethodExtractor.cs | 202 ------ .../Discovery/TypeDefinitionExtractor.cs | 129 ---- .../ExtractedContainerDefinition.cs | 28 - .../Extractors/ExtractedOperationMethod.cs | 62 -- .../Extractors/ExtractedOperationParameter.cs | 51 -- .../Extractors/ExtractedTypeDefinition.cs | 75 -- .../Extractors/GenerationResults.cs | 28 - ...IncrementalDataPortalInterfaceGenerator.cs | 152 ---- Source/Csla/BusinessBindingListBase.cs | 2 +- Source/Csla/BusinessDocumentBase.cs | 2 +- Source/Csla/BusinessListBase.cs | 2 +- Source/Csla/CommandBase.cs | 2 +- Source/Csla/Csla.csproj | 17 +- .../CslaIgnorePropertyAttribute.cs | 0 .../CslaImplementPropertiesAttribute.cs | 0 ...laImplementPropertiesInterfaceAttribute.cs | 0 Source/Csla/DynamicBindingListBase.cs | 2 +- Source/Csla/DynamicListBase.cs | 2 +- Source/Csla/NameValueListBase.cs | 2 +- Source/Csla/ReadOnlyBase.cs | 2 +- Source/Csla/ReadOnlyBindingListBase.cs | 2 +- Source/Csla/ReadOnlyListBase.cs | 2 +- .../Csla/Server/DataPortalOperationHelper.cs | 86 +++ Source/Csla/buildTransitive/Csla.targets | 15 + Source/csla.build.sln | 414 ----------- Source/csla.test.sln | 137 ---- ...AutoImplementProperties.TestObjects.csproj | 1 - ...utoImplementProperties.CSharp.Tests.csproj | 2 +- .../DataPortalOperationsGeneratorTests.cs | 660 ++++++++++++++++++ .../DataPortalOperationsTestHelper.cs | 98 +++ ...nBase_1.DataPortalOperations.g.verified.cs | 22 + ...sonEdit.DataPortalOperations.g.verified.cs | 82 +++ ...sonEdit.DataPortalOperations.g.verified.cs | 110 +++ ...sonEdit.DataPortalOperations.g.verified.cs | 56 ++ ...ineItem.DataPortalOperations.g.verified.cs | 134 ++++ ...sonEdit.DataPortalOperations.g.verified.cs | 134 ++++ ...sonEdit.DataPortalOperations.g.verified.cs | 54 ++ ...nBase_1.DataPortalOperations.g.verified.cs | 22 + ...sonEdit.DataPortalOperations.g.verified.cs | 54 ++ ...sonEdit.DataPortalOperations.g.verified.cs | 60 ++ ...rTests.DuplicateOperationName.verified.txt | 32 + ...Command.DataPortalOperations.g.verified.cs | 54 ++ ...sonEdit.DataPortalOperations.g.verified.cs | 37 + ...sonEdit.DataPortalOperations.g.verified.cs | 71 ++ ...ookup_1.DataPortalOperations.g.verified.cs | 61 ++ ...sonEdit.DataPortalOperations.g.verified.cs | 58 ++ ...sonEdit.DataPortalOperations.g.verified.cs | 58 ++ ...sonEdit.DataPortalOperations.g.verified.cs | 68 ++ ...sonEdit.DataPortalOperations.g.verified.cs | 57 ++ ...sonEdit.DataPortalOperations.g.verified.cs | 58 ++ ...sonEdit.DataPortalOperations.g.verified.cs | 58 ++ ...sonEdit.DataPortalOperations.g.verified.cs | 56 ++ ...sonEdit.DataPortalOperations.g.verified.cs | 71 ++ ...sonEdit.DataPortalOperations.g.verified.cs | 71 ++ ...sonEdit.DataPortalOperations.g.verified.cs | 89 +++ ...sonEdit.DataPortalOperations.g.verified.cs | 54 ++ ...sonEdit.DataPortalOperations.g.verified.cs | 54 ++ .../StageCachingTester.cs | 125 ++++ ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...e=True#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...e=True#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...e=True#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...e=True#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...e=True#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...e=True#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...e=True#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...e=True#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...e=True#BusinessBaseTestClass.g.received.cs | 17 - ...e=True#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...e=True#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...e=True#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...e=True#BusinessBaseTestClass.g.received.cs | 17 - ...e=True#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...e=True#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...e=True#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...e=True#BusinessBaseTestClass.g.received.cs | 17 - ...=False#BusinessBaseTestClass.g.received.cs | 17 - ...torTests.MyTestMethod#BOTest.g.received.cs | 17 - ...r.DataPortalInterfaces.CSharp.Tests.csproj | 39 -- .../DataPortalInterfaceGeneratorTests.cs | 312 --------- ...sonEdit.DataPortalOperations.g.verified.cs | 39 -- ...ineItem.DataPortalOperations.g.verified.cs | 105 --- ...sonEdit.DataPortalOperations.g.verified.cs | 105 --- ...sonEdit.DataPortalOperations.g.verified.cs | 39 -- ...Command.DataPortalOperations.g.verified.cs | 39 -- ...sonEdit.DataPortalOperations.g.verified.cs | 54 -- ...sonEdit.DataPortalOperations.g.verified.cs | 51 -- ...sonEdit.DataPortalOperations.g.verified.cs | 42 -- ...sonEdit.DataPortalOperations.g.verified.cs | 41 -- ...sonEdit.DataPortalOperations.g.verified.cs | 41 -- ...sonEdit.DataPortalOperations.g.verified.cs | 54 -- ...sonEdit.DataPortalOperations.g.verified.cs | 39 -- ...sonEdit.DataPortalOperations.g.verified.cs | 39 -- .../Helpers/TestHelper.cs | 91 --- Source/tests/Csla.test/Csla.Tests.csproj | 1 - 157 files changed, 4169 insertions(+), 4086 deletions(-) delete mode 100644 Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.Attributes.CSharp/Csla.Generator.AutoImplementProperties.Attributes.CSharp.csproj create mode 100644 Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsBuilder.cs create mode 100644 Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs create mode 100644 Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/IncrementalDataPortalOperationsGenerator.cs create mode 100644 Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/LocationInfo.cs create mode 100644 Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/OperationMethodModel.cs create mode 100644 Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/OperationParameterModel.cs create mode 100644 Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/OperationTypeModel.cs create mode 100644 Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs create mode 100644 Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/TrackingNames.cs create mode 100644 Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/Internals/EquatableArray.cs create mode 100644 Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/Internals/HashCode.cs delete mode 100644 Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/tools/install.ps1 delete mode 100644 Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/tools/uninstall.ps1 delete mode 100644 Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Csla.Generator.DataPortalInterfaces.CSharp.csproj delete mode 100644 Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/DataPortalInterfaceBuilder.cs delete mode 100644 Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Discovery/ContainerDefinitionsExtractor.cs delete mode 100644 Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Discovery/DefinitionExtractionContext.cs delete mode 100644 Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Discovery/OperationMethodExtractor.cs delete mode 100644 Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Discovery/TypeDefinitionExtractor.cs delete mode 100644 Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/ExtractedContainerDefinition.cs delete mode 100644 Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/ExtractedOperationMethod.cs delete mode 100644 Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/ExtractedOperationParameter.cs delete mode 100644 Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/ExtractedTypeDefinition.cs delete mode 100644 Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/GenerationResults.cs delete mode 100644 Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/IncrementalDataPortalInterfaceGenerator.cs rename Source/{Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.Attributes.CSharp => Csla}/CslaIgnorePropertyAttribute.cs (100%) rename Source/{Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.Attributes.CSharp => Csla}/CslaImplementPropertiesAttribute.cs (100%) rename Source/{Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.Attributes.CSharp => Csla}/CslaImplementPropertiesInterfaceAttribute.cs (100%) create mode 100644 Source/Csla/Server/DataPortalOperationHelper.cs create mode 100644 Source/Csla/buildTransitive/Csla.targets create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsGeneratorTests.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsTestHelper.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AbstractClass#TestApp.PersonBase_1.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ArrayNullableGenericCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AsyncMethod#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DeleteWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DerivedFromBaseWithOperations#TestApp.PersonBase_1.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DerivedFromBaseWithOperations#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName.verified.txt create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ExecuteCommand#TestApp.MyCommand.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ExistingNamedMapping#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.FileScopedNamespace#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.GenericBusinessObject#TestApp.Lookup_1.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InjectBeforeCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.KeyedInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.MultipleOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.NestedClass#TestApp.Outer.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ParamsArrayAndKeywordNames#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.PartialClassAcrossFiles#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.RootAndChildAttributes#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.RunLocalAndValueTask#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.SingleCreateNoParams#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.SingleFetchWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/StageCachingTester.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=bool-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=bool[]-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=bool[]_isNullable=True#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=bool_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=byte-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=byte[]-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=byte[]_isNullable=True#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=byte_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=char-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=char[]-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=char[]_isNullable=True#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=char_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=decimal-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=decimal[]-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=decimal[]_isNullable=True#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=decimal_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=double-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=double[]-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=double[]_isNullable=True#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=double_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=float-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=float[]-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=float[]_isNullable=True#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=float_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=int-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=int[]-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=int[]_isNullable=True#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=int_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=long-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=long[]-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=long[]_isNullable=True#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=long_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=object-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=object[]-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=object[]_isNullable=True#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=object_isNullable=True#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=sbyte-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=sbyte[]-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=sbyte[]_isNullable=True#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=sbyte_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=short-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=short[]-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=short[]_isNullable=True#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=short_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=string-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=string[]-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=string[]_isNullable=True#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=string_isNullable=True#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=uint-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=uint[]-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=uint[]_isNullable=True#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=uint_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ulong-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ulong[]-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ulong[]_isNullable=True#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ulong_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ushort-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ushort[]-_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ushort[]_isNullable=True#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ushort_isNullable=False#BusinessBaseTestClass.g.received.cs delete mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.MyTestMethod#BOTest.g.received.cs delete mode 100644 Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests.csproj delete mode 100644 Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/DataPortalInterfaceGeneratorTests.cs delete mode 100644 Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.AsyncMethod#TestApp.PersonEdit.DataPortalOperations.g.verified.cs delete mode 100644 Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs delete mode 100644 Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs delete mode 100644 Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.DeleteWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs delete mode 100644 Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.ExecuteCommand#TestApp.MyCommand.DataPortalOperations.g.verified.cs delete mode 100644 Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.FileScopedNamespace#TestApp.PersonEdit.DataPortalOperations.g.verified.cs delete mode 100644 Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.MultipleOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs delete mode 100644 Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.NestedClass#TestApp.Outer.PersonEdit.DataPortalOperations.g.verified.cs delete mode 100644 Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs delete mode 100644 Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs delete mode 100644 Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.RootAndChildAttributes#TestApp.PersonEdit.DataPortalOperations.g.verified.cs delete mode 100644 Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.SingleCreateNoParams#TestApp.PersonEdit.DataPortalOperations.g.verified.cs delete mode 100644 Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.SingleFetchWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs delete mode 100644 Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/TestHelper.cs diff --git a/Source/Csla.Benchmarks/Csla.Benchmarks.csproj b/Source/Csla.Benchmarks/Csla.Benchmarks.csproj index dba023d63f..b07477a812 100644 --- a/Source/Csla.Benchmarks/Csla.Benchmarks.csproj +++ b/Source/Csla.Benchmarks/Csla.Benchmarks.csproj @@ -10,6 +10,6 @@ - + diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.Attributes.CSharp/Csla.Generator.AutoImplementProperties.Attributes.CSharp.csproj b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.Attributes.CSharp/Csla.Generator.AutoImplementProperties.Attributes.CSharp.csproj deleted file mode 100644 index db90f85d61..0000000000 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.Attributes.CSharp/Csla.Generator.AutoImplementProperties.Attributes.CSharp.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - - netstandard2.0 - false - false - false - false - false - false - false - false - false - false - ..\..\..\..\..\Bin - ..\..\..\..\..\bin\packages\ - false - true - - diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/AutoImplement/ExtractedTypeDefinition.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/AutoImplement/ExtractedTypeDefinition.cs index 454cc8c442..6d22d89c27 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/AutoImplement/ExtractedTypeDefinition.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/AutoImplement/ExtractedTypeDefinition.cs @@ -1,4 +1,5 @@ -using Csla.Generator.AutoImplementProperties.CSharp.AutoImplement; +using Csla.Generator.AutoImplementProperties.CSharp; +using Csla.Generator.AutoImplementProperties.CSharp.AutoImplement; /// /// The definition of a type, extracted from the syntax tree provided by Roslyn diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/Csla.Generator.AutoImplementProperties.CSharp.csproj b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/Csla.Generator.AutoImplementProperties.CSharp.csproj index fa8842bd4a..fb2304ec0b 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/Csla.Generator.AutoImplementProperties.CSharp.csproj +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/Csla.Generator.AutoImplementProperties.CSharp.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -13,33 +13,20 @@ false false true + $(NoWarn);RS2008 ..\..\..\..\..\Bin ..\..\..\..\..\bin\packages\ Csla.Generator.AutoImplementProperties.CSharp Csla.Generator.AutoImplementProperties.CSharp - Csla.Generator.AutoImplementProperties.CSharp - CSLA .NET Generator AutoImplementProperties for CSharp + CSLA .NET source generators for CSharp (bundled in the Csla package) true True - CSLA .NET Generators - CSLA;Roslyn;Generator - - - true - false - true + + false - - - - - - - - - \ No newline at end of file + diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsBuilder.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsBuilder.cs new file mode 100644 index 0000000000..189aa4de05 --- /dev/null +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsBuilder.cs @@ -0,0 +1,339 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// Builds generated source for a type's data portal operations +//----------------------------------------------------------------------- + +using System.CodeDom.Compiler; +using Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations.Models; + +namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations +{ + /// + /// Builds the partial type declaration that exposes a type's data portal + /// operation methods through a nested IDataPortalOperations interface + /// and, for concrete types, implements IDataPortalOperationMapping + /// and IDataPortalOperationNamedMapping. + /// + internal static class DataPortalOperationsBuilder + { + private const string HelperType = "global::Csla.Server.DataPortalOperationHelper"; + private const string OperationsLocal = "__operations"; + + /// + /// Build the generated source for a type. + /// + public static string Build(OperationTypeModel model) + { + var type = model.Type; + var memberNames = GetInterfaceMemberNames(model.Methods); + + using var stringWriter = new StringWriter(); + var writer = new IndentedTextWriter(stringWriter, " "); + + writer.WriteLine("//"); + writer.WriteLine("#nullable enable"); + writer.WriteLine("#pragma warning disable CS1998 // Async method lacks 'await' operators"); + writer.WriteLineNoTabs(string.Empty); + + if (type.Namespace.Length > 0) + { + writer.WriteLine($"namespace {type.Namespace}"); + OpenBlock(writer); + } + + foreach (var container in type.ContainerDeclarations) + { + writer.WriteLine(container); + OpenBlock(writer); + } + + var emitMapping = !type.IsAbstract && !type.ImplementsOperationMapping; + var emitNamedMapping = !type.IsAbstract && !type.ImplementsNamedOperationMapping; + + var baseList = new List { $"{type.FullyQualifiedName}.{OperationDiscovery.OperationsInterfaceName}" }; + if (emitMapping) + baseList.Add("global::Csla.Server.IDataPortalOperationMapping"); + if (emitNamedMapping) + baseList.Add("global::Csla.Server.IDataPortalOperationNamedMapping"); + + writer.WriteLine($"{type.TypeDeclaration} : {string.Join(", ", baseList)}"); + OpenBlock(writer); + + AppendOperationsInterface(writer, model, memberNames); + writer.WriteLineNoTabs(string.Empty); + AppendExplicitImplementations(writer, model, memberNames); + + if (emitMapping) + { + writer.WriteLineNoTabs(string.Empty); + AppendInvokeOperationAsync(writer, model, memberNames); + } + + if (emitNamedMapping) + { + writer.WriteLineNoTabs(string.Empty); + AppendInvokeNamedOperationAsync(writer, model, memberNames); + } + + CloseBlock(writer); + + foreach (var _ in type.ContainerDeclarations) + CloseBlock(writer); + + if (type.Namespace.Length > 0) + CloseBlock(writer); + + return stringWriter.ToString(); + } + + /// + /// Selects the method used for each operation name in name-based + /// dispatch. Like the runtime method resolution, when several methods + /// share an operation name (they differ only in injected parameters) + /// the one with the most injected parameters wins; ties go to the + /// first declared. Returns the selected methods and any collisions. + /// + public static (List Selected, List<(OperationMethodModel Winner, OperationMethodModel Loser)> Collisions) + SelectNamedDispatchMethods(EquatableArray methods) + { + var selected = new List(); + var collisions = new List<(OperationMethodModel, OperationMethodModel)>(); + foreach (var group in methods.Where(m => m.CanDispatchByName).GroupBy(m => m.OperationName)) + { + var ordered = group.ToList(); + var winner = ordered[0]; + foreach (var candidate in ordered.Skip(1)) + { + if (candidate.InjectCount > winner.InjectCount) + winner = candidate; + } + selected.Add(winner); + foreach (var loser in ordered.Where(m => !ReferenceEquals(m, winner))) + collisions.Add((winner, loser)); + } + return (selected, collisions); + } + + private static Dictionary GetInterfaceMemberNames(EquatableArray methods) + { + var result = new Dictionary(ReferenceEqualityComparer.Instance); + var signatures = new HashSet(); + foreach (var method in methods) + { + var parameterTypes = string.Join(",", method.Parameters.Select(p => p.RefKindPrefix + p.PatternTypeDisplay + (p.IsNullableValueType ? "?" : string.Empty))); + var name = method.OperationName; + var suffix = 1; + while (!signatures.Add($"{name}({parameterTypes})")) + { + suffix++; + name = $"{method.OperationName}_{suffix}"; + } + result[method] = name; + } + return result; + } + + private static void AppendOperationsInterface(IndentedTextWriter writer, OperationTypeModel model, Dictionary memberNames) + { + writer.WriteLine("/// "); + writer.WriteLine("/// Data portal operation methods declared by this type."); + writer.WriteLine("/// Generated by CSLA .NET so the methods are statically referenced."); + writer.WriteLine("/// "); + writer.WriteLine("[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]"); + var modifiers = model.Type.HidesInheritedOperationsInterface ? "internal new interface" : "internal interface"; + writer.WriteLine($"{modifiers} {OperationDiscovery.OperationsInterfaceName}"); + OpenBlock(writer); + foreach (var method in model.Methods) + writer.WriteLine($"{method.ReturnTypeDisplay} {memberNames[method]}({FormatParameters(method)});"); + CloseBlock(writer); + } + + private static void AppendExplicitImplementations(IndentedTextWriter writer, OperationTypeModel model, Dictionary memberNames) + { + foreach (var method in model.Methods) + { + var arguments = string.Join(", ", method.Parameters.Select(p => ArgumentPrefix(p) + p.Name)); + writer.WriteLine($"{method.ReturnTypeDisplay} {OperationDiscovery.OperationsInterfaceName}.{memberNames[method]}({FormatParameters(method)}) => {method.MethodName}({arguments});"); + } + } + + private static void AppendInvokeOperationAsync(IndentedTextWriter writer, OperationTypeModel model, Dictionary memberNames) + { + writer.WriteLine("async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync("); + writer.Indent++; + writer.WriteLine("global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider)"); + writer.Indent--; + OpenBlock(writer); + + // Methods sharing an operation name differ only in injected parameters; + // only the one the runtime would select takes part in dispatch. + var (_, collisions) = SelectNamedDispatchMethods(model.Methods); + var losers = new HashSet(collisions.Select(c => c.Loser), ReferenceEqualityComparer.Instance); + var dispatchable = model.Methods.Where(m => m.CanDispatch && !losers.Contains(m)).ToList(); + if (dispatchable.Count > 0) + writer.WriteLine($"var {OperationsLocal} = ({OperationDiscovery.OperationsInterfaceName})this;"); + + var candidateIndex = 0; + var isFirst = true; + foreach (var kind in OperationDiscovery.OperationKinds) + { + var methods = dispatchable.Where(m => m.Kind == kind).ToList(); + if (methods.Count == 0) + continue; + + writer.WriteLine($"{(isFirst ? "if" : "else if")} (operationType == typeof(global::Csla.{kind}Attribute))"); + isFirst = false; + OpenBlock(writer); + foreach (var method in methods) + { + // When several methods of this kind take the same number of criteria, + // require exact runtime types so a less specific overload declared + // first cannot capture the call; anything else falls back to reflection. + var requireExactTypes = methods.Count(m => m.CriteriaCount == method.CriteriaCount) > 1; + AppendDispatch(writer, method, memberNames[method], candidateIndex++, byName: false, requireExactTypes); + } + CloseBlock(writer); + } + + writer.WriteLine("throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria);"); + CloseBlock(writer); + } + + private static void AppendInvokeNamedOperationAsync(IndentedTextWriter writer, OperationTypeModel model, Dictionary memberNames) + { + writer.WriteLine("async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync("); + writer.Indent++; + writer.WriteLine("string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider)"); + writer.Indent--; + OpenBlock(writer); + + var (selected, _) = SelectNamedDispatchMethods(model.Methods); + if (selected.Count > 0) + { + writer.WriteLine($"var {OperationsLocal} = ({OperationDiscovery.OperationsInterfaceName})this;"); + writer.WriteLine("switch (operationName)"); + OpenBlock(writer); + var candidateIndex = 0; + foreach (var method in selected) + { + writer.WriteLine($"case \"{method.OperationName}\":"); + writer.Indent++; + AppendDispatch(writer, method, memberNames[method], candidateIndex++, byName: true, requireExactTypes: false); + writer.WriteLine("break;"); + writer.Indent--; + } + CloseBlock(writer); + } + + writer.WriteLine("throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria);"); + CloseBlock(writer); + } + + private static void AppendDispatch(IndentedTextWriter writer, OperationMethodModel method, string memberName, int candidateIndex, bool byName, bool requireExactTypes) + { + var criteria = method.CriteriaParameters.ToList(); + var conditions = new List(); + if (criteria.Count == 0) + { + conditions.Add("criteria is null or { Length: 0 }"); + } + else + { + conditions.Add($"criteria is {{ Length: {criteria.Count} }}"); + for (var i = 0; i < criteria.Count; i++) + { + var parameter = criteria[i]; + var variable = CriteriaVariable(candidateIndex, i); + if (byName && parameter.AcceptsNull) + { + conditions.Add($"(criteria[{i}] is null || criteria[{i}] is {parameter.PatternTypeDisplay})"); + } + else + { + conditions.Add($"criteria[{i}] is {parameter.PatternTypeDisplay} {variable}"); + if (requireExactTypes && !parameter.IsExactTypeMatch) + conditions.Add($"{variable}.GetType() == typeof({parameter.PatternTypeDisplay})"); + } + } + } + + writer.WriteLine($"if ({string.Join(" && ", conditions)})"); + OpenBlock(writer); + + if (method.IsAsync) + writer.WriteLine($"{HelperType}.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, \"{method.MethodName.TrimStart('@')}\");"); + + var arguments = new List(); + var criteriaIndex = 0; + var injectIndex = 0; + foreach (var parameter in method.Parameters) + { + if (parameter.IsInjected) + { + var variable = $"__c{candidateIndex}i{injectIndex++}"; + writer.WriteLine($"var {variable} = {FormatServiceResolution(parameter)};"); + arguments.Add(ArgumentPrefix(parameter) + variable); + } + else + { + var i = criteriaIndex++; + string argument; + if (byName && parameter.AcceptsNull) + argument = parameter.IsNullableValueType + ? $"({parameter.TypeDisplay})criteria[{i}]" + : $"({parameter.TypeDisplay})criteria[{i}]!"; + else + argument = CriteriaVariable(candidateIndex, i); + arguments.Add(ArgumentPrefix(parameter) + argument); + } + } + + var call = $"{OperationsLocal}.{memberName}({string.Join(", ", arguments)})"; + writer.WriteLine(method.IsAsync ? $"await {call}.ConfigureAwait(false);" : $"{call};"); + writer.WriteLine("return;"); + CloseBlock(writer); + } + + private static string FormatServiceResolution(OperationParameterModel parameter) + { + var serviceType = $"typeof({parameter.PatternTypeDisplay})"; + var resolution = parameter.ServiceKeyExpression is null + ? $"{HelperType}.GetService(serviceProvider, {serviceType}, {(parameter.AllowNull ? "true" : "false")})" + : $"{HelperType}.GetKeyedService(serviceProvider, {serviceType}, {parameter.ServiceKeyExpression}, {(parameter.AllowNull ? "true" : "false")})"; + return parameter.AllowNull + ? $"({parameter.TypeDisplay})({resolution})" + : $"({parameter.TypeDisplay})({resolution})!"; + } + + private static string CriteriaVariable(int candidateIndex, int criteriaIndex) => $"__c{candidateIndex}p{criteriaIndex}"; + + private static string FormatParameters(OperationMethodModel method) + => string.Join(", ", method.Parameters.Select(p => $"{p.RefKindPrefix}{p.TypeDisplay} {p.Name}")); + + private static string ArgumentPrefix(OperationParameterModel parameter) => parameter.RefKindPrefix; + + private static void OpenBlock(IndentedTextWriter writer) + { + writer.WriteLine("{"); + writer.Indent++; + } + + private static void CloseBlock(IndentedTextWriter writer) + { + writer.Indent--; + writer.WriteLine("}"); + } + + private sealed class ReferenceEqualityComparer : IEqualityComparer + { + public static readonly ReferenceEqualityComparer Instance = new(); + + public bool Equals(OperationMethodModel? x, OperationMethodModel? y) => ReferenceEquals(x, y); + + public int GetHashCode(OperationMethodModel obj) => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj); + } + } +} diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs new file mode 100644 index 0000000000..2bc2d7f686 --- /dev/null +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs @@ -0,0 +1,77 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// Diagnostics reported by the data portal generators +//----------------------------------------------------------------------- + +using Microsoft.CodeAnalysis; + +namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations +{ + /// + /// Diagnostic descriptors reported by the data portal operations + /// and data portal extensions generators. + /// + internal static class DataPortalOperationsDiagnostics + { + private const string Category = "Csla.DataPortal"; + + public const string DuplicateOperationNameId = "CSLADP002"; + public const string GenericExtensionsNotGeneratedId = "CSLADP003"; + public const string InvalidExtensionsTargetId = "CSLADP004"; + public const string ExtensionNameHiddenId = "CSLADP005"; + public const string InaccessibleParameterTypeId = "CSLADP006"; + + public static readonly DiagnosticDescriptor DuplicateOperationName = new( + id: DuplicateOperationNameId, + title: "Data portal operation methods share an operation name", + messageFormat: "Methods '{0}' and '{1}' on '{2}' both map to data portal operation '{3}'; name-based dispatch uses '{0}'", + category: Category, + defaultSeverity: DiagnosticSeverity.Info, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor GenericExtensionsNotGenerated = new( + id: GenericExtensionsNotGeneratedId, + title: "Data portal extensions are not generated for generic types", + messageFormat: "Data portal extension methods are not generated for '{0}' because it is generic or nested in a generic type", + category: Category, + defaultSeverity: DiagnosticSeverity.Info, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor InvalidExtensionsTarget = new( + id: InvalidExtensionsTargetId, + title: "Invalid target for DataPortalExtensions", + messageFormat: "Data portal extension methods are not generated for '{0}': {1}", + category: Category, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor ExtensionNameHidden = new( + id: ExtensionNameHiddenId, + title: "Generated data portal extension would be hidden", + messageFormat: "The data portal extension method '{0}' for '{1}' is not generated because it would be hidden by the '{2}' instance method; set DataPortalExtensions.Prefix to give the generated methods distinct names", + category: Category, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor InaccessibleParameterType = new( + id: InaccessibleParameterTypeId, + title: "Operation parameter type is not accessible", + messageFormat: "The data portal extension method for '{0}' cannot be generated because parameter '{1}' has type '{2}', which is not accessible outside its containing type", + category: Category, + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static DiagnosticDescriptor GetDescriptor(string id) => id switch + { + DuplicateOperationNameId => DuplicateOperationName, + GenericExtensionsNotGeneratedId => GenericExtensionsNotGenerated, + InvalidExtensionsTargetId => InvalidExtensionsTarget, + ExtensionNameHiddenId => ExtensionNameHidden, + InaccessibleParameterTypeId => InaccessibleParameterType, + _ => throw new ArgumentOutOfRangeException(nameof(id), id, null) + }; + } +} diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/IncrementalDataPortalOperationsGenerator.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/IncrementalDataPortalOperationsGenerator.cs new file mode 100644 index 0000000000..3aec07868e --- /dev/null +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/IncrementalDataPortalOperationsGenerator.cs @@ -0,0 +1,98 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// Source generator for data portal operation interfaces and dispatch +//----------------------------------------------------------------------- + +using System.Collections.Immutable; +using System.Text; +using Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations.Models; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; + +namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations +{ + /// + /// Incremental source generator that, for every partial class declaring + /// data portal operation methods, generates a nested internal + /// IDataPortalOperations interface naming each operation method and, + /// for concrete classes, implementations of IDataPortalOperationMapping + /// and IDataPortalOperationNamedMapping. + /// + [Generator(LanguageNames.CSharp)] + public class IncrementalDataPortalOperationsGenerator : IIncrementalGenerator + { + /// + public void Initialize(IncrementalGeneratorInitializationContext context) + { + var types = GetOperationTypes(context) + .SelectMany(static (types, _) => types) + .Where(static t => t.Type.IsPartial) + .WithTrackingName(TrackingNames.OperationTypes); + + context.RegisterSourceOutput(types, static (spc, model) => + { + var (_, collisions) = DataPortalOperationsBuilder.SelectNamedDispatchMethods(model.Methods); + foreach (var (winner, loser) in collisions) + { + spc.ReportDiagnostic(Diagnostic.Create( + DataPortalOperationsDiagnostics.DuplicateOperationName, + loser.Location?.ToLocation(), + winner.MethodDisplay, loser.MethodDisplay, model.Type.TypeName, winner.OperationName)); + } + + spc.AddSource( + $"{model.Type.HintName}.DataPortalOperations.g.cs", + SourceText.From(DataPortalOperationsBuilder.Build(model), Encoding.UTF8)); + }); + } + + /// + /// Discovers every type declaring data portal operation methods, grouped + /// by type so a partial class spread across several files yields one model. + /// + internal static IncrementalValueProvider> GetOperationTypes(IncrementalGeneratorInitializationContext context) + { + IncrementalValueProvider>? all = null; + foreach (var kind in OperationDiscovery.OperationKinds) + { + var entries = context.SyntaxProvider.ForAttributeWithMetadataName( + OperationDiscovery.GetAttributeMetadataName(kind), + predicate: static (node, _) => node is MethodDeclarationSyntax, + transform: (ctx, ct) => OperationDiscovery.ExtractMethod(ctx, kind, ct)) + .Where(static e => e is not null) + .Select(static (e, _) => e!) + .WithTrackingName(TrackingNames.ExtractOperationMethods) + .Collect(); + + all = all is null + ? entries + : all.Value.Combine(entries).Select(static (pair, _) => pair.Left.AddRange(pair.Right)); + } + + return all!.Value + .Select(static (entries, ct) => GroupByType(entries, ct)) + .WithTrackingName(TrackingNames.GroupOperationTypes); + } + + private static EquatableArray GroupByType(ImmutableArray entries, CancellationToken ct) + { + var result = new List(); + foreach (var group in entries.GroupBy(e => e.Type.MetadataName).OrderBy(g => g.Key, StringComparer.Ordinal)) + { + ct.ThrowIfCancellationRequested(); + var header = group.First().Type with { IsPartial = group.All(e => e.Type.IsPartial) }; + var methods = group + .Select(e => e.Method) + .OrderBy(m => m.Location?.FilePath ?? string.Empty, StringComparer.Ordinal) + .ThenBy(m => m.Location?.TextSpan.Start ?? 0) + .ThenBy(m => Array.IndexOf(OperationDiscovery.OperationKinds, m.Kind)); + result.Add(new OperationTypeModel(header, new EquatableArray(methods))); + } + return new EquatableArray(result); + } + } +} diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/LocationInfo.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/LocationInfo.cs new file mode 100644 index 0000000000..e429abd486 --- /dev/null +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/LocationInfo.cs @@ -0,0 +1,41 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// Equatable source location for generator diagnostics +//----------------------------------------------------------------------- + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations.Models +{ + /// + /// An equatable representation of a source location, safe to keep in + /// incremental generator models (unlike ). + /// + internal sealed record LocationInfo(string FilePath, TextSpan TextSpan, LinePositionSpan LineSpan) + { + public Location ToLocation() => Location.Create(FilePath, TextSpan, LineSpan); + + public static LocationInfo? From(Location? location) + { + if (location is null || location.SourceTree is null) + return null; + return new LocationInfo(location.SourceTree.FilePath, location.SourceSpan, location.GetLineSpan().Span); + } + } + + /// + /// An equatable diagnostic to be reported by a generator. + /// + internal sealed record DiagnosticInfo(string Id, LocationInfo? Location, EquatableArray MessageArgs) + { + public Diagnostic ToDiagnostic() + { + var descriptor = DataPortalOperationsDiagnostics.GetDescriptor(Id); + return Diagnostic.Create(descriptor, Location?.ToLocation(), MessageArgs.Cast().ToArray()); + } + } +} diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/OperationMethodModel.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/OperationMethodModel.cs new file mode 100644 index 0000000000..0a6b9b4dd0 --- /dev/null +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/OperationMethodModel.cs @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// A data portal operation method for one operation attribute +//----------------------------------------------------------------------- + +namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations.Models +{ + /// + /// A data portal operation method as seen through one operation + /// attribute. A method carrying several operation attributes + /// yields one model per attribute. + /// + internal sealed record OperationMethodModel + { + public required string MethodName { get; init; } + + /// Method signature for diagnostics, e.g. "Fetch(int)". + public string MethodDisplay { get; init; } = string.Empty; + + /// Operation kind, e.g. "Fetch" or "FetchChild". + public required string Kind { get; init; } + + /// Fully qualified return type, e.g. "void". + public required string ReturnTypeDisplay { get; init; } + + /// Returns Task, Task<T>, ValueTask or ValueTask<T>. + public bool IsAsync { get; init; } + + /// All parameters, criteria and injected, in declaration order. + public required EquatableArray Parameters { get; init; } + + /// Deterministic operation name, e.g. "Fetch__Int32". + public required string OperationName { get; init; } + + public bool RunLocal { get; init; } + + public bool NoExtension { get; init; } + + public LocationInfo? Location { get; init; } + + public IEnumerable CriteriaParameters => Parameters.Where(p => !p.IsInjected); + + public int CriteriaCount => Parameters.Count(p => !p.IsInjected); + + public int InjectCount => Parameters.Count(p => p.IsInjected); + + /// + /// The method can be invoked from generated dispatch code. Methods + /// taking a single object[] (which receive the whole criteria array) + /// or ref/out parameters are left to the reflection-based path. + /// + public bool CanDispatch => + !Parameters.Any(p => p.RefKindPrefix is "ref " or "out ") + && !(Parameters.Count == 1 && Parameters[0].IsObjectArray); + + /// + /// The method can take part in name-based dispatch. When criteria + /// reference a type parameter the generated name cannot match the + /// runtime-computed name. + /// + public bool CanDispatchByName => CanDispatch && !CriteriaParameters.Any(p => p.ContainsTypeParameter); + } +} diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/OperationParameterModel.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/OperationParameterModel.cs new file mode 100644 index 0000000000..5bbc6fef40 --- /dev/null +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/OperationParameterModel.cs @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// A parameter of a data portal operation method +//----------------------------------------------------------------------- + +namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations.Models +{ + /// + /// Effective accessibility of a parameter type, as seen from + /// a top-level type in the same assembly. + /// + internal enum TypeVisibility + { + Public, + Internal, + Private + } + + /// + /// A parameter of a data portal operation method. + /// + internal sealed record OperationParameterModel + { + /// The parameter name, escaped with @ when it is a keyword. + public required string Name { get; init; } + + /// Fully qualified type including nullable annotation. + public required string TypeDisplay { get; init; } + + /// + /// Type usable in an is pattern: nullable annotation removed and + /// unwrapped. + /// + public required string PatternTypeDisplay { get; init; } + + /// Type key used to compute the operation name. + public required string TypeKey { get; init; } + + /// "ref ", "out ", "in " or empty. + public string RefKindPrefix { get; init; } = string.Empty; + + public bool IsNullableValueType { get; init; } + + /// The runtime value may legitimately be null (reference type or Nullable). + public bool AcceptsNull { get; init; } + + /// + /// An is pattern on this type can only match that exact runtime + /// type (value types and sealed classes). + /// + public bool IsExactTypeMatch { get; init; } + + public bool ContainsTypeParameter { get; init; } + + public bool IsParams { get; init; } + + /// True when the type is exactly object[] (a CSLA params-array operation). + public bool IsObjectArray { get; init; } + + public bool IsInjected { get; init; } + + public bool AllowNull { get; init; } + + /// C# expression for the service key of a keyed [Inject], or null. + public string? ServiceKeyExpression { get; init; } + + /// C# expression for the default value (without "= "), or null. + public string? DefaultValueExpression { get; init; } + + public TypeVisibility Visibility { get; init; } + } +} diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/OperationTypeModel.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/OperationTypeModel.cs new file mode 100644 index 0000000000..4d91e4e82a --- /dev/null +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/OperationTypeModel.cs @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// A type that declares data portal operation methods +//----------------------------------------------------------------------- + +namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations.Models +{ + /// + /// Type-level information about a class declaring data portal + /// operation methods. Computed independently for each method so a + /// partial class spread across files is grouped by . + /// + internal sealed record OperationTypeHeader + { + /// Fully qualified metadata name, used as the grouping key. + public required string MetadataName { get; init; } + + /// Namespace, or empty for the global namespace. + public required string Namespace { get; init; } + + /// Declarations of containing types, outermost first, e.g. "partial class Outer". + public required EquatableArray ContainerDeclarations { get; init; } + + /// Declaration of the type, e.g. "partial class PersonEdit<T>". + public required string TypeDeclaration { get; init; } + + /// Fully qualified type name with type parameters, e.g. "global::NS.PersonEdit". + public required string FullyQualifiedName { get; init; } + + /// Simple type name without type parameters. + public required string TypeName { get; init; } + + /// Containing type names, outermost first. + public required EquatableArray ContainerNames { get; init; } + + /// File name friendly unique name, e.g. "NS.Outer.PersonEdit_1". + public required string HintName { get; init; } + + /// The type and all containing types are declared partial. + public bool IsPartial { get; init; } + + public bool IsAbstract { get; init; } + + /// The type or a containing type is generic. + public bool IsGeneric { get; init; } + + /// An accessible base type already exposes a nested IDataPortalOperations. + public bool HidesInheritedOperationsInterface { get; init; } + + public bool ImplementsOperationMapping { get; init; } + + public bool ImplementsNamedOperationMapping { get; init; } + } + + /// + /// One discovered operation method together with its type header. + /// + internal sealed record OperationMethodEntry(OperationTypeHeader Type, OperationMethodModel Method); + + /// + /// A type and all of its data portal operation methods. + /// + internal sealed record OperationTypeModel(OperationTypeHeader Type, EquatableArray Methods); +} diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs new file mode 100644 index 0000000000..8c9b592c92 --- /dev/null +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs @@ -0,0 +1,441 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// Extracts data portal operation models from Roslyn symbols +//----------------------------------------------------------------------- + +using System.Globalization; +using System.Text; +using Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations.Models; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations +{ + /// + /// Extracts equatable data portal operation models from Roslyn symbols. + /// + internal static class OperationDiscovery + { + /// + /// The data portal operation kinds, in the order used for generated dispatch. + /// + public static readonly string[] OperationKinds = + [ + "Create", + "Fetch", + "Insert", + "Update", + "Execute", + "Delete", + "DeleteSelf", + "CreateChild", + "FetchChild", + "InsertChild", + "UpdateChild", + "DeleteSelfChild", + "ExecuteChild" + ]; + + public const string InjectAttributeName = "Csla.InjectAttribute"; + public const string RunLocalAttributeName = "Csla.RunLocalAttribute"; + public const string NoDataPortalExtensionAttributeName = "Csla.NoDataPortalExtensionAttribute"; + public const string OperationsInterfaceName = "IDataPortalOperations"; + + private const string OperationMappingName = "Csla.Server.IDataPortalOperationMapping"; + private const string NamedOperationMappingName = "Csla.Server.IDataPortalOperationNamedMapping"; + + /// + /// Fully qualified type with nullable reference annotations. + /// + internal static readonly SymbolDisplayFormat TypeFormat = SymbolDisplayFormat.FullyQualifiedFormat + .AddMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier); + + /// + /// Fully qualified type without nullable reference annotations, with tuples + /// expanded so the name can be used in an is pattern. + /// + internal static readonly SymbolDisplayFormat PatternFormat = SymbolDisplayFormat.FullyQualifiedFormat + .AddMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.ExpandValueTuple); + + private static readonly SymbolDisplayFormat MethodDisplayFormat = new( + memberOptions: SymbolDisplayMemberOptions.IncludeParameters, + parameterOptions: SymbolDisplayParameterOptions.IncludeType, + miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + public static string GetAttributeMetadataName(string kind) => $"Csla.{kind}Attribute"; + + /// + /// Extract the model for one operation method, as seen through one operation attribute. + /// + public static OperationMethodEntry? ExtractMethod(GeneratorAttributeSyntaxContext context, string kind, CancellationToken ct) + { + if (context.TargetSymbol is not IMethodSymbol method || !IsCandidateMethod(method)) + return null; + + var type = method.ContainingType; + if (type is null || type.TypeKind != TypeKind.Class) + return null; + + ct.ThrowIfCancellationRequested(); + + var compilation = context.SemanticModel.Compilation; + var header = BuildHeader(type, context.TargetNode, compilation); + var model = BuildMethod(method, kind); + return new OperationMethodEntry(header, model); + } + + /// + /// A method that the generator includes in the operations interface. + /// + internal static bool IsCandidateMethod(IMethodSymbol method) + => method.MethodKind == MethodKind.Ordinary && !method.IsStatic && !method.IsGenericMethod; + + private static OperationTypeHeader BuildHeader(INamedTypeSymbol type, SyntaxNode methodNode, Compilation compilation) + { + var containers = new List(); + for (var container = type.ContainingType; container is not null; container = container.ContainingType) + containers.Insert(0, container); + + var hintParts = new List(); + var @namespace = type.ContainingNamespace is { IsGlobalNamespace: false } ns ? ns.ToDisplayString() : string.Empty; + if (@namespace.Length > 0) + hintParts.Add(@namespace); + foreach (var container in containers) + hintParts.Add(GetHintSegment(container)); + hintParts.Add(GetHintSegment(type)); + + var isGeneric = type.TypeParameters.Length > 0 || containers.Any(c => c.TypeParameters.Length > 0); + + return new OperationTypeHeader + { + MetadataName = GetFullMetadataName(type), + Namespace = @namespace, + ContainerDeclarations = new EquatableArray(containers.Select(GetPartialDeclaration)), + ContainerNames = new EquatableArray(containers.Select(c => c.Name)), + TypeDeclaration = GetPartialDeclaration(type), + FullyQualifiedName = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), + TypeName = type.Name, + HintName = string.Join(".", hintParts), + IsPartial = IsPartialAllTheWay(methodNode), + IsAbstract = type.IsAbstract, + IsGeneric = isGeneric, + HidesInheritedOperationsInterface = HidesInheritedOperationsInterface(type, compilation), + ImplementsOperationMapping = type.Interfaces.Any(i => i.ToDisplayString() == OperationMappingName), + ImplementsNamedOperationMapping = type.Interfaces.Any(i => i.ToDisplayString() == NamedOperationMappingName) + }; + } + + private static OperationMethodModel BuildMethod(IMethodSymbol method, string kind) + { + var parameters = method.Parameters.Select(BuildParameter).ToArray(); + var criteriaKeys = parameters.Where(p => !p.IsInjected).Select(p => p.TypeKey).ToArray(); + var operationName = criteriaKeys.Length == 0 ? kind : kind + "__" + string.Join("_", criteriaKeys); + + return new OperationMethodModel + { + MethodName = EscapeIdentifier(method.Name), + MethodDisplay = method.ToDisplayString(MethodDisplayFormat), + Kind = kind, + ReturnTypeDisplay = method.ReturnType.ToDisplayString(TypeFormat), + IsAsync = IsAwaitable(method.ReturnType), + Parameters = new EquatableArray(parameters), + OperationName = operationName, + RunLocal = HasAttribute(method, RunLocalAttributeName), + NoExtension = HasAttribute(method, NoDataPortalExtensionAttributeName), + Location = LocationInfo.From(method.Locations.FirstOrDefault()) + }; + } + + private static OperationParameterModel BuildParameter(IParameterSymbol parameter) + { + var type = parameter.Type; + var isNullableValueType = type is INamedTypeSymbol { OriginalDefinition.SpecialType: SpecialType.System_Nullable_T }; + var patternType = isNullableValueType ? ((INamedTypeSymbol)type).TypeArguments[0] : type; + + var isInjected = false; + var allowNull = false; + string? serviceKey = null; + foreach (var attribute in parameter.GetAttributes()) + { + if (!IsOrDerivesFrom(attribute.AttributeClass, InjectAttributeName)) + continue; + isInjected = true; + foreach (var namedArgument in attribute.NamedArguments) + { + if (namedArgument.Key == "AllowNull" && namedArgument.Value.Value is bool value) + allowNull = value; + else if (namedArgument.Key == "Key" && !namedArgument.Value.IsNull) + serviceKey = FormatTypedConstant(namedArgument.Value); + } + } + + return new OperationParameterModel + { + Name = EscapeIdentifier(parameter.Name), + TypeDisplay = type.ToDisplayString(TypeFormat), + PatternTypeDisplay = patternType.ToDisplayString(PatternFormat), + TypeKey = GetOperationTypeKey(type), + RefKindPrefix = parameter.RefKind switch + { + RefKind.Ref => "ref ", + RefKind.Out => "out ", + RefKind.In => "in ", + _ => string.Empty + }, + IsNullableValueType = isNullableValueType, + AcceptsNull = type.IsReferenceType || isNullableValueType, + IsExactTypeMatch = patternType.IsValueType || (patternType.IsSealed && patternType.TypeKind != TypeKind.Array), + ContainsTypeParameter = ContainsTypeParameter(type), + IsParams = parameter.IsParams, + IsObjectArray = type is IArrayTypeSymbol { Rank: 1, ElementType.SpecialType: SpecialType.System_Object }, + IsInjected = isInjected, + AllowNull = allowNull, + ServiceKeyExpression = serviceKey, + DefaultValueExpression = parameter.HasExplicitDefaultValue ? FormatDefaultValue(type, parameter.ExplicitDefaultValue) : null, + Visibility = GetVisibility(type) + }; + } + + /// + /// Computes a deterministic type key for use in operation names. + /// Must produce the same result as DataPortalOperationNameHelper.GetTypeKey(Type) at runtime. + /// Arrays: elementType + "Array" (e.g. "Int32Array"). + /// Generic types: MetadataName with backtick replaced, then all type + /// arguments including those of containing types (e.g. "List_1_Int32"). + /// Other types: MetadataName (e.g. "Int32", "String"). + /// + internal static string GetOperationTypeKey(ITypeSymbol typeSymbol) + { + if (typeSymbol is IArrayTypeSymbol arrayType) + return GetOperationTypeKey(arrayType.ElementType) + "Array"; + + if (typeSymbol.TypeKind == TypeKind.Dynamic) + return "Object"; + + if (typeSymbol is INamedTypeSymbol namedType && namedType.IsGenericType) + { + var typeArguments = new List(); + CollectTypeArguments(namedType, typeArguments); + var baseName = namedType.MetadataName.Replace('`', '_'); + return baseName + "_" + string.Join("_", typeArguments.Select(GetOperationTypeKey)); + } + + return typeSymbol.MetadataName; + } + + private static void CollectTypeArguments(INamedTypeSymbol type, List typeArguments) + { + if (type.ContainingType is not null) + CollectTypeArguments(type.ContainingType, typeArguments); + typeArguments.AddRange(type.TypeArguments); + } + + /// + /// Returns true when the type will expose a generated operations interface. + /// + internal static bool WillGenerateOperationsInterface(INamedTypeSymbol type) + { + if (type.TypeKind != TypeKind.Class) + return false; + + var hasOperations = type.GetMembers() + .OfType() + .Any(m => IsCandidateMethod(m) && m.GetAttributes().Any(a => IsOperationAttribute(a.AttributeClass))); + if (!hasOperations) + return false; + + return type.DeclaringSyntaxReferences.All(r => r.GetSyntax() is TypeDeclarationSyntax declaration && IsPartialAllTheWay(declaration)); + } + + internal static bool IsOperationAttribute(INamedTypeSymbol? attributeClass) + { + if (attributeClass is null || attributeClass.ContainingNamespace?.ToDisplayString() != "Csla") + return false; + var name = attributeClass.Name; + return name.EndsWith("Attribute") && OperationKinds.Contains(name.Substring(0, name.Length - "Attribute".Length)); + } + + private static bool HidesInheritedOperationsInterface(INamedTypeSymbol type, Compilation compilation) + { + for (var baseType = type.BaseType; baseType is not null && baseType.SpecialType != SpecialType.System_Object; baseType = baseType.BaseType) + { + var definition = baseType.OriginalDefinition; + if (definition.GetTypeMembers(OperationsInterfaceName).Any(t => compilation.IsSymbolAccessibleWithin(t, compilation.Assembly))) + return true; + if (SymbolEqualityComparer.Default.Equals(definition.ContainingAssembly, compilation.Assembly) && WillGenerateOperationsInterface(definition)) + return true; + } + return false; + } + + /// + /// The type declaration containing the node and all of its + /// containing type declarations have the partial modifier. + /// + internal static bool IsPartialAllTheWay(SyntaxNode node) + { + var found = false; + for (var current = node; current is not null; current = current.Parent) + { + if (current is TypeDeclarationSyntax typeDeclaration) + { + found = true; + if (!typeDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword)) + return false; + } + } + return found; + } + + private static string GetPartialDeclaration(INamedTypeSymbol type) + { + var keyword = type switch + { + { IsRecord: true, TypeKind: TypeKind.Struct } => "record struct", + { IsRecord: true } => "record", + { TypeKind: TypeKind.Struct } => "struct", + { TypeKind: TypeKind.Interface } => "interface", + _ => "class" + }; + var builder = new StringBuilder("partial ").Append(keyword).Append(' ').Append(EscapeIdentifier(type.Name)); + if (type.TypeParameters.Length > 0) + builder.Append('<').Append(string.Join(", ", type.TypeParameters.Select(t => EscapeIdentifier(t.Name)))).Append('>'); + return builder.ToString(); + } + + private static string GetHintSegment(INamedTypeSymbol type) + => type.TypeParameters.Length > 0 ? $"{type.Name}_{type.TypeParameters.Length}" : type.Name; + + private static string GetFullMetadataName(INamedTypeSymbol type) + { + var name = type.MetadataName; + for (var container = type.ContainingType; container is not null; container = container.ContainingType) + name = container.MetadataName + "+" + name; + if (type.ContainingNamespace is { IsGlobalNamespace: false } ns) + name = ns.ToDisplayString() + "." + name; + return name; + } + + private static bool IsAwaitable(ITypeSymbol returnType) + { + if (returnType is not INamedTypeSymbol named) + return false; + var definition = named.OriginalDefinition; + if (definition.ContainingNamespace?.ToDisplayString() != "System.Threading.Tasks") + return false; + return definition.MetadataName is "Task" or "Task`1" or "ValueTask" or "ValueTask`1"; + } + + private static bool HasAttribute(ISymbol symbol, string attributeName) + => symbol.GetAttributes().Any(a => a.AttributeClass?.ToDisplayString() == attributeName); + + private static bool IsOrDerivesFrom(INamedTypeSymbol? type, string fullName) + { + for (var current = type; current is not null; current = current.BaseType) + { + if (current.ToDisplayString() == fullName) + return true; + } + return false; + } + + private static bool ContainsTypeParameter(ITypeSymbol type) => type switch + { + ITypeParameterSymbol => true, + IArrayTypeSymbol array => ContainsTypeParameter(array.ElementType), + INamedTypeSymbol named => named.TypeArguments.Any(ContainsTypeParameter) + || (named.ContainingType is not null && ContainsTypeParameter(named.ContainingType)), + _ => false + }; + + internal static TypeVisibility GetVisibility(ITypeSymbol type) + { + switch (type) + { + case IArrayTypeSymbol array: + return GetVisibility(array.ElementType); + case INamedTypeSymbol named: + var result = TypeVisibility.Public; + for (var current = named; current is not null; current = current.ContainingType) + result = Min(result, FromAccessibility(current.DeclaredAccessibility)); + foreach (var typeArgument in named.TypeArguments) + result = Min(result, GetVisibility(typeArgument)); + return result; + default: + return TypeVisibility.Public; + } + + static TypeVisibility Min(TypeVisibility left, TypeVisibility right) => (TypeVisibility)Math.Max((int)left, (int)right); + + static TypeVisibility FromAccessibility(Accessibility accessibility) => accessibility switch + { + Accessibility.Public => TypeVisibility.Public, + Accessibility.Internal or Accessibility.ProtectedOrInternal => TypeVisibility.Internal, + _ => TypeVisibility.Private + }; + } + + internal static string EscapeIdentifier(string identifier) + => SyntaxFacts.GetKeywordKind(identifier) != SyntaxKind.None ? "@" + identifier : identifier; + + private static string FormatTypedConstant(TypedConstant constant) + { + if (constant.Kind == TypedConstantKind.Type && constant.Value is ITypeSymbol typeValue) + return $"typeof({typeValue.ToDisplayString(PatternFormat)})"; + if (constant.Kind == TypedConstantKind.Enum && constant.Type is ITypeSymbol enumType) + return FormatEnumValue(enumType, constant.Value); + return FormatPrimitive(constant.Value) ?? constant.ToCSharpString(); + } + + private static string FormatDefaultValue(ITypeSymbol type, object? value) + { + var isNullableValueType = type is INamedTypeSymbol { OriginalDefinition.SpecialType: SpecialType.System_Nullable_T }; + var targetType = isNullableValueType ? ((INamedTypeSymbol)type).TypeArguments[0] : type; + + if (value is null) + return type.IsReferenceType || isNullableValueType ? "null" : "default"; + + if (targetType.TypeKind == TypeKind.Enum) + return FormatEnumValue(targetType, value); + + return FormatPrimitive(value) ?? "default"; + } + + private static string FormatEnumValue(ITypeSymbol enumType, object? value) + { + var enumName = enumType.ToDisplayString(PatternFormat); + var member = enumType.GetMembers() + .OfType() + .FirstOrDefault(f => f.HasConstantValue && Equals(f.ConstantValue, value)); + return member is not null + ? $"{enumName}.{EscapeIdentifier(member.Name)}" + : $"({enumName})({Convert.ToString(value, CultureInfo.InvariantCulture)})"; + } + + private static string? FormatPrimitive(object? value) => value switch + { + null => "null", + string s => SymbolDisplay.FormatLiteral(s, quote: true), + char c => SymbolDisplay.FormatLiteral(c, quote: true), + bool b => b ? "true" : "false", + float f when float.IsNaN(f) => "float.NaN", + float f when float.IsPositiveInfinity(f) => "float.PositiveInfinity", + float f when float.IsNegativeInfinity(f) => "float.NegativeInfinity", + float f => f.ToString("R", CultureInfo.InvariantCulture) + "F", + double d when double.IsNaN(d) => "double.NaN", + double d when double.IsPositiveInfinity(d) => "double.PositiveInfinity", + double d when double.IsNegativeInfinity(d) => "double.NegativeInfinity", + double d => d.ToString("R", CultureInfo.InvariantCulture) + "D", + decimal m => m.ToString(CultureInfo.InvariantCulture) + "M", + long l => l.ToString(CultureInfo.InvariantCulture) + "L", + ulong ul => ul.ToString(CultureInfo.InvariantCulture) + "UL", + uint ui => ui.ToString(CultureInfo.InvariantCulture) + "U", + int or short or ushort or byte or sbyte => Convert.ToString(value, CultureInfo.InvariantCulture), + _ => null + }; + } +} diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/TrackingNames.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/TrackingNames.cs new file mode 100644 index 0000000000..defcac6413 --- /dev/null +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/TrackingNames.cs @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// Tracking names for validating incremental generator caching +//----------------------------------------------------------------------- + +namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations +{ + /// + /// Names of incremental pipeline steps, used by tests to verify caching. + /// + public static class TrackingNames + { + /// Extraction of individual operation methods. + public const string ExtractOperationMethods = nameof(ExtractOperationMethods); + + /// Grouping of operation methods by containing type. + public const string GroupOperationTypes = nameof(GroupOperationTypes); + + /// Per-type models for operations generation. + public const string OperationTypes = nameof(OperationTypes); + + /// Extraction of types marked with DataPortalExtensions. + public const string ExtractExtensionTypes = nameof(ExtractExtensionTypes); + + /// Per-type models for extension generation. + public const string ExtensionTypes = nameof(ExtensionTypes); + } +} diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/Internals/EquatableArray.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/Internals/EquatableArray.cs new file mode 100644 index 0000000000..a1e0fbf7b4 --- /dev/null +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/Internals/EquatableArray.cs @@ -0,0 +1,73 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// Immutable array with value equality for incremental generator models +//----------------------------------------------------------------------- + +using System.Collections; + +namespace Csla.Generator.AutoImplementProperties.CSharp +{ + /// + /// An immutable array with value (sequence) equality, so incremental + /// generator models that contain collections compare correctly and + /// the pipeline can cache unchanged results. + /// + internal readonly struct EquatableArray : IEquatable>, IReadOnlyList + where T : IEquatable + { + public static readonly EquatableArray Empty = new([]); + + private readonly T[]? _array; + + public EquatableArray(T[] array) + { + _array = array; + } + + public EquatableArray(IEnumerable items) + { + _array = items.ToArray(); + } + + public T this[int index] => (_array ?? [])[index]; + + public int Count => _array?.Length ?? 0; + + public bool IsEmpty => Count == 0; + + public bool Equals(EquatableArray other) + { + var left = _array ?? []; + var right = other._array ?? []; + if (left.Length != right.Length) + return false; + for (var i = 0; i < left.Length; i++) + { + if (!EqualityComparer.Default.Equals(left[i], right[i])) + return false; + } + return true; + } + + public override bool Equals(object? obj) => obj is EquatableArray other && Equals(other); + + public override int GetHashCode() + { + var hash = new HashCode(); + foreach (var item in _array ?? []) + hash.Add(item); + return hash.ToHashCode(); + } + + public IEnumerator GetEnumerator() => ((IEnumerable)(_array ?? [])).GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public static bool operator ==(EquatableArray left, EquatableArray right) => left.Equals(right); + + public static bool operator !=(EquatableArray left, EquatableArray right) => !left.Equals(right); + } +} diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/Internals/HashCode.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/Internals/HashCode.cs new file mode 100644 index 0000000000..23490caa43 --- /dev/null +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/Internals/HashCode.cs @@ -0,0 +1,106 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// Minimal hash code combiner for netstandard2.0 +//----------------------------------------------------------------------- + +namespace Csla.Generator.AutoImplementProperties.CSharp +{ + /// + /// Minimal replacement for System.HashCode, which is not available + /// on netstandard2.0. Keeps the generator free of extra dependencies + /// so it ships as a single analyzer assembly. + /// + internal struct HashCode + { + private const int Seed = unchecked((int)2166136261); + private const int Prime = 16777619; + + private int _value; + private bool _started; + + public void Add(T value) + { + if (!_started) + { + _value = Seed; + _started = true; + } + _value = unchecked((_value ^ (value?.GetHashCode() ?? 0)) * Prime); + } + + public readonly int ToHashCode() => _started ? _value : Seed; + + public static int Combine(T1 v1) + { + var hash = new HashCode(); + hash.Add(v1); + return hash.ToHashCode(); + } + + public static int Combine(T1 v1, T2 v2) + { + var hash = new HashCode(); + hash.Add(v1); + hash.Add(v2); + return hash.ToHashCode(); + } + + public static int Combine(T1 v1, T2 v2, T3 v3) + { + var hash = new HashCode(); + hash.Add(v1); + hash.Add(v2); + hash.Add(v3); + return hash.ToHashCode(); + } + + public static int Combine(T1 v1, T2 v2, T3 v3, T4 v4) + { + var hash = new HashCode(); + hash.Add(v1); + hash.Add(v2); + hash.Add(v3); + hash.Add(v4); + return hash.ToHashCode(); + } + + public static int Combine(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5) + { + var hash = new HashCode(); + hash.Add(v1); + hash.Add(v2); + hash.Add(v3); + hash.Add(v4); + hash.Add(v5); + return hash.ToHashCode(); + } + + public static int Combine(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6) + { + var hash = new HashCode(); + hash.Add(v1); + hash.Add(v2); + hash.Add(v3); + hash.Add(v4); + hash.Add(v5); + hash.Add(v6); + return hash.ToHashCode(); + } + + public static int Combine(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7) + { + var hash = new HashCode(); + hash.Add(v1); + hash.Add(v2); + hash.Add(v3); + hash.Add(v4); + hash.Add(v5); + hash.Add(v6); + hash.Add(v7); + return hash.ToHashCode(); + } + } +} diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/tools/install.ps1 b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/tools/install.ps1 deleted file mode 100644 index 5fdd376ac6..0000000000 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/tools/install.ps1 +++ /dev/null @@ -1,49 +0,0 @@ -param($installPath, $toolsPath, $package, $project) - -$generatorsPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers" ) * -Resolve - -foreach($generatorsPath in $generatorsPaths) -{ - # Install the language agnostic source generators. - if (Test-Path $generatorsPath) - { - foreach ($generatorFilePath in Get-ChildItem $generatorsPath -Filter *.dll) - { - if($project.Object.AnalyzerReferences) - { - $project.Object.AnalyzerReferences.Add($generatorFilePath.FullName) - } - } - } -} - -# $project.Type gives the language name like (C# or VB.NET) -$languageFolder = "" -if($project.Type -eq "C#") -{ - $languageFolder = "cs" -} -if($project.Type -eq "VB.NET") -{ - $languageFolder = "vb" -} -if($languageFolder -eq "") -{ - return -} - -foreach($generatorsPath in $generatorsPaths) -{ - # Install language specific source generators. - $languageGeneratorsPath = join-path $generatorsPath $languageFolder - if (Test-Path $languageGeneratorsPath) - { - foreach ($generatorFilePath in Get-ChildItem $languageGeneratorsPath -Filter *.dll) - { - if($project.Object.AnalyzerReferences) - { - $project.Object.AnalyzerReferences.Add($generatorFilePath.FullName) - } - } - } -} diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/tools/uninstall.ps1 b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/tools/uninstall.ps1 deleted file mode 100644 index dc5357b118..0000000000 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/tools/uninstall.ps1 +++ /dev/null @@ -1,56 +0,0 @@ -param($installPath, $toolsPath, $package, $project) - -$generatorsPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers" ) * -Resolve - -foreach($generatorsPath in $generatorsPaths) -{ - # Uninstall the language agnostic source generators. - if (Test-Path $generatorsPath) - { - foreach ($generatorFilePath in Get-ChildItem $generatorsPath -Filter *.dll) - { - if($project.Object.AnalyzerReferences) - { - $project.Object.AnalyzerReferences.Remove($generatorFilePath.FullName) - } - } - } -} - -# $project.Type gives the language name like (C# or VB.NET) -$languageFolder = "" -if($project.Type -eq "C#") -{ - $languageFolder = "cs" -} -if($project.Type -eq "VB.NET") -{ - $languageFolder = "vb" -} -if($languageFolder -eq "") -{ - return -} - -foreach($generatorsPath in $generatorsPaths) -{ - # Uninstall language specific source generators. - $languageGeneratorsPath = join-path $generatorsPath $languageFolder - if (Test-Path $languageGeneratorsPath) - { - foreach ($generatorFilePath in Get-ChildItem $languageGeneratorsPath -Filter *.dll) - { - if($project.Object.AnalyzerReferences) - { - try - { - $project.Object.AnalyzerReferences.Remove($generatorFilePath.FullName) - } - catch - { - - } - } - } - } -} diff --git a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Csla.Generator.DataPortalInterfaces.CSharp.csproj b/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Csla.Generator.DataPortalInterfaces.CSharp.csproj deleted file mode 100644 index 52f266960b..0000000000 --- a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Csla.Generator.DataPortalInterfaces.CSharp.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - netstandard2.0 - true - false - false - false - false - false - false - false - false - false - true - RS2008 - ..\..\..\..\..\Bin - ..\..\..\..\..\bin\packages\ - Csla.Generator.DataPortalInterfaces.CSharp - Csla.Generator.DataPortalInterfaces.CSharp - CSLA .NET Generator DataPortalInterfaces for CSharp - true - True - false - - - - - - - diff --git a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/DataPortalInterfaceBuilder.cs b/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/DataPortalInterfaceBuilder.cs deleted file mode 100644 index 0e136807f6..0000000000 --- a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/DataPortalInterfaceBuilder.cs +++ /dev/null @@ -1,287 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Marimer LLC. All rights reserved. -// Website: https://cslanet.com -// -// Builds generated source for data portal interface implementation -//----------------------------------------------------------------------- - -using System.CodeDom.Compiler; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Csla.Generator.DataPortalInterfaces.CSharp.Extractors; - -namespace Csla.Generator.DataPortalInterfaces.CSharp -{ - - /// - /// Builds the generated source code that provides an explicit - /// implementation of IDataPortalOperationMapping for a business type - /// - internal class DataPortalInterfaceBuilder - { - - /// - /// Build the partial type definition with the interface implementation - /// - internal GenerationResults BuildPartialTypeDefinition(ExtractedTypeDefinition typeDefinition) - { - using var stringWriter = new StringWriter(); - var textWriter = new IndentedTextWriter(stringWriter, " "); - - textWriter.WriteLine("//"); - textWriter.WriteLine("#nullable enable"); - textWriter.WriteLine(); - - AppendContainerDefinitions(textWriter, typeDefinition); - AppendTypeDefinition(textWriter, typeDefinition); - AppendBlockStart(textWriter); - - AppendInvokeOperationAsyncMethod(textWriter, typeDefinition); - textWriter.WriteLine(); - AppendInvokeNamedOperationAsyncMethod(textWriter, typeDefinition); - - AppendBlockEnd(textWriter); - AppendContainerDefinitionClosures(textWriter, typeDefinition); - - return new GenerationResults - { - FullyQualifiedName = typeDefinition.FullyQualifiedName, - GeneratedSource = stringWriter.ToString() - }; - } - - private void AppendBlockStart(IndentedTextWriter textWriter) - { - textWriter.WriteLine("{"); - textWriter.Indent++; - } - - private void AppendBlockEnd(IndentedTextWriter textWriter) - { - textWriter.Indent--; - textWriter.WriteLine("}"); - } - - private void AppendContainerDefinitions(IndentedTextWriter textWriter, ExtractedTypeDefinition typeDefinition) - { - foreach (var containerDefinition in typeDefinition.ContainerDefinitions) - { - textWriter.WriteLine(containerDefinition.FullDefinition); - AppendBlockStart(textWriter); - } - } - - private void AppendContainerDefinitionClosures(IndentedTextWriter textWriter, ExtractedTypeDefinition typeDefinition) - { - foreach (var containerDefinition in typeDefinition.ContainerDefinitions) - { - AppendBlockEnd(textWriter); - } - } - - private void AppendTypeDefinition(IndentedTextWriter textWriter, ExtractedTypeDefinition typeDefinition) - { - textWriter.Write(typeDefinition.Scope); - textWriter.Write(" partial class "); - textWriter.Write(typeDefinition.TypeName); - textWriter.Write(typeDefinition.TypeParameters); - textWriter.WriteLine(" : Csla.Server.IDataPortalOperationMapping, Csla.Server.IDataPortalOperationNamedMapping"); - } - - private void AppendInvokeOperationAsyncMethod(IndentedTextWriter textWriter, ExtractedTypeDefinition typeDefinition) - { - textWriter.WriteLine("async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync("); - textWriter.Indent++; - textWriter.WriteLine("global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider)"); - textWriter.Indent--; - AppendBlockStart(textWriter); - - // Group methods by their operation attribute types - var methodsByAttribute = GroupMethodsByAttribute(typeDefinition.OperationMethods); - - var isFirst = true; - foreach (var group in methodsByAttribute) - { - if (isFirst) - { - textWriter.Write("if"); - isFirst = false; - } - else - { - textWriter.Write("else if"); - } - - textWriter.WriteLine($" (operationType == typeof({group.Key}))"); - AppendBlockStart(textWriter); - - foreach (var method in group.Value) - { - AppendMethodDispatch(textWriter, method); - } - - AppendBlockEnd(textWriter); - } - - textWriter.WriteLine("throw new Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria);"); - - AppendBlockEnd(textWriter); - } - - private void AppendMethodDispatch(IndentedTextWriter textWriter, ExtractedOperationMethod method) - { - var criteriaCount = method.CriteriaParameters.Count; - - if (criteriaCount == 0) - { - textWriter.WriteLine("if (criteria is null or { Length: 0 })"); - } - else - { - // Build pattern match: criteria is { Length: N } && criteria[0] is Type name && ... - var conditions = new List(); - conditions.Add($"criteria is {{ Length: {criteriaCount} }}"); - - for (int i = 0; i < criteriaCount; i++) - { - var param = method.CriteriaParameters[i]; - conditions.Add($"criteria[{i}] is {param.TypeDisplayName} p{i}_{SanitizeTypeName(param.TypeFullName)}"); - } - - textWriter.WriteLine($"if ({string.Join(" && ", conditions)})"); - } - - AppendBlockStart(textWriter); - - // Resolve injected parameters - foreach (var inject in method.InjectParameters) - { - if (inject.AllowNull) - { - textWriter.WriteLine($"var {inject.Name} = ({inject.TypeDisplayName}?)serviceProvider.GetService(typeof({inject.TypeDisplayName}));"); - } - else - { - textWriter.WriteLine($"var {inject.Name} = ({inject.TypeDisplayName})(serviceProvider.GetService(typeof({inject.TypeDisplayName})) ?? throw new global::System.InvalidOperationException($\"No service for type '{{typeof({inject.TypeDisplayName})}}' has been registered.\"));"); - } - } - - // Build method call - var args = new List(); - - // All parameters in original order (criteria + inject interleaved) - // We need to reconstruct the original parameter order - // The criteria params are in order, inject params are in order - // We emit them by building from both lists - var allParams = GetAllParametersInOrder(method); - foreach (var p in allParams) - { - if (p.IsInjected) - { - args.Add(p.Name); - } - else - { - var criteriaIndex = method.CriteriaParameters.IndexOf(p); - args.Add($"p{criteriaIndex}_{SanitizeTypeName(p.TypeFullName)}"); - } - } - - var argString = string.Join(", ", args); - - if (method.IsAsync) - { - textWriter.WriteLine($"await {method.MethodName}({argString}).ConfigureAwait(false);"); - } - else - { - textWriter.WriteLine($"{method.MethodName}({argString});"); - } - - textWriter.WriteLine("return;"); - AppendBlockEnd(textWriter); - } - - private static IList GetAllParametersInOrder(ExtractedOperationMethod method) - { - // Return all parameters in their original declaration order - // by merging criteria and inject lists - var all = new List(); - all.AddRange(method.CriteriaParameters); - all.AddRange(method.InjectParameters); - return all; - } - - private static Dictionary> GroupMethodsByAttribute( - IList methods) - { - var result = new Dictionary>(); - - foreach (var method in methods) - { - foreach (var attr in method.OperationAttributeNames) - { - if (!result.TryGetValue(attr, out var list)) - { - list = new List(); - result[attr] = list; - } - list.Add(method); - } - } - - return result; - } - - private void AppendInvokeNamedOperationAsyncMethod(IndentedTextWriter textWriter, ExtractedTypeDefinition typeDefinition) - { - textWriter.WriteLine("async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync("); - textWriter.Indent++; - textWriter.WriteLine("string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider)"); - textWriter.Indent--; - AppendBlockStart(textWriter); - - textWriter.WriteLine("switch (operationName)"); - AppendBlockStart(textWriter); - - // Collect all unique operation names across all methods - var emittedNames = new HashSet(); - foreach (var method in typeDefinition.OperationMethods) - { - foreach (var attr in method.OperationAttributeNames) - { - var operationName = method.GetOperationName(attr); - if (emittedNames.Add(operationName)) - { - textWriter.WriteLine($"case \"{operationName}\":"); - textWriter.Indent++; - AppendMethodDispatch(textWriter, method); - textWriter.WriteLine("break;"); - textWriter.Indent--; - } - } - } - - AppendBlockEnd(textWriter); - - textWriter.WriteLine("throw new Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria);"); - - AppendBlockEnd(textWriter); - } - - private static string SanitizeTypeName(string typeName) - { - // Remove global:: prefix and dots, convert to safe identifier - return typeName - .Replace("global::", "") - .Replace(".", "_") - .Replace("<", "_") - .Replace(">", "_") - .Replace(",", "_") - .Replace(" ", "") - .Replace("?", "Nullable"); - } - } -} diff --git a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Discovery/ContainerDefinitionsExtractor.cs b/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Discovery/ContainerDefinitionsExtractor.cs deleted file mode 100644 index c128ed7de3..0000000000 --- a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Discovery/ContainerDefinitionsExtractor.cs +++ /dev/null @@ -1,110 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Marimer LLC. All rights reserved. -// Website: https://cslanet.com -// -// Extract container definitions for a type -//----------------------------------------------------------------------- - -using System.Collections.Generic; -using System.Text; -using Csla.Generator.DataPortalInterfaces.CSharp.Extractors; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Csla.Generator.DataPortalInterfaces.CSharp.Discovery -{ - - /// - /// Extract the container definitions for a type declaration - /// - internal static class ContainerDefinitionsExtractor - { - - /// - /// Get the container definitions for a type declaration, - /// from outermost to innermost - /// - public static IReadOnlyList GetContainerDefinitions( - DefinitionExtractionContext context, - TypeDeclarationSyntax typeDeclaration) - { - var containers = new List(); - var parent = typeDeclaration.Parent; - - while (parent != null) - { - if (parent is TypeDeclarationSyntax parentType) - { - containers.Insert(0, BuildTypeContainer(parentType)); - } - else if (parent is BaseNamespaceDeclarationSyntax namespaceDecl) - { - containers.Insert(0, BuildNamespaceContainer(namespaceDecl)); - break; - } - parent = parent.Parent; - } - - return containers; - } - - private static ExtractedContainerDefinition BuildNamespaceContainer(BaseNamespaceDeclarationSyntax namespaceDecl) - { - var fullNamespace = GetFullNamespace(namespaceDecl); - return new ExtractedContainerDefinition - { - Name = fullNamespace, - FullDefinition = $"namespace {fullNamespace}" - }; - } - - private static string GetFullNamespace(BaseNamespaceDeclarationSyntax namespaceDecl) - { - var parts = new List(); - var current = namespaceDecl; - - while (current != null) - { - parts.Insert(0, current.Name.ToString()); - current = current.Parent as BaseNamespaceDeclarationSyntax; - } - - return string.Join('.', parts); - } - - private static ExtractedContainerDefinition BuildTypeContainer(TypeDeclarationSyntax typeDecl) - { - var sb = new StringBuilder(); - - foreach (var modifier in typeDecl.Modifiers) - { - if (modifier.IsKind(SyntaxKind.PublicKeyword) || - modifier.IsKind(SyntaxKind.InternalKeyword) || - modifier.IsKind(SyntaxKind.ProtectedKeyword) || - modifier.IsKind(SyntaxKind.PrivateKeyword)) - { - sb.Append(modifier.ValueText); - sb.Append(' '); - } - } - - sb.Append("partial "); - sb.Append(typeDecl.Keyword.ValueText); - sb.Append(' '); - sb.Append(typeDecl.Identifier.Text); - - if (typeDecl.TypeParameterList != null) - { - sb.Append(typeDecl.TypeParameterList.ToString()); - } - - return new ExtractedContainerDefinition - { - Name = typeDecl.Identifier.Text, - FullDefinition = sb.ToString() - }; - } - } -} diff --git a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Discovery/DefinitionExtractionContext.cs b/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Discovery/DefinitionExtractionContext.cs deleted file mode 100644 index a2b00831e1..0000000000 --- a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Discovery/DefinitionExtractionContext.cs +++ /dev/null @@ -1,52 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Marimer LLC. All rights reserved. -// Website: https://cslanet.com -// -// Wraps a SemanticModel for definition extraction -//----------------------------------------------------------------------- - -using Microsoft.CodeAnalysis; - -namespace Csla.Generator.DataPortalInterfaces.CSharp.Discovery -{ - - /// - /// Wraps a SemanticModel for definition extraction, - /// providing helper methods for symbol resolution - /// - internal class DefinitionExtractionContext - { - private readonly SemanticModel _semanticModel; - - /// - /// Create a new DefinitionExtractionContext - /// - /// The Roslyn SemanticModel to use - public DefinitionExtractionContext(SemanticModel semanticModel) - { - _semanticModel = semanticModel; - } - - /// - /// Gets the SemanticModel - /// - public SemanticModel SemanticModel => _semanticModel; - - /// - /// Get the symbol for a syntax node - /// - public ISymbol? GetDeclaredSymbol(SyntaxNode node) - { - return _semanticModel.GetDeclaredSymbol(node); - } - - /// - /// Get the type info for a syntax node - /// - public TypeInfo GetTypeInfo(SyntaxNode node) - { - return _semanticModel.GetTypeInfo(node); - } - } -} diff --git a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Discovery/OperationMethodExtractor.cs b/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Discovery/OperationMethodExtractor.cs deleted file mode 100644 index 9625c3d2b2..0000000000 --- a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Discovery/OperationMethodExtractor.cs +++ /dev/null @@ -1,202 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Marimer LLC. All rights reserved. -// Website: https://cslanet.com -// -// Extract data portal operation methods from a type -//----------------------------------------------------------------------- - -using System.Collections.Generic; -using System.Linq; -using Csla.Generator.DataPortalInterfaces.CSharp.Extractors; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Csla.Generator.DataPortalInterfaces.CSharp.Discovery -{ - - /// - /// Extract data portal operation methods from a type declaration - /// - internal static class OperationMethodExtractor - { - /// - /// All CSLA data portal operation attribute fully-qualified names - /// - private static readonly HashSet DataPortalAttributeNames = new HashSet - { - "Csla.CreateAttribute", - "Csla.FetchAttribute", - "Csla.InsertAttribute", - "Csla.UpdateAttribute", - "Csla.ExecuteAttribute", - "Csla.DeleteAttribute", - "Csla.DeleteSelfAttribute", - "Csla.CreateChildAttribute", - "Csla.FetchChildAttribute", - "Csla.InsertChildAttribute", - "Csla.UpdateChildAttribute", - "Csla.DeleteSelfChildAttribute", - "Csla.ExecuteChildAttribute" - }; - - private const string InjectAttributeName = "Csla.InjectAttribute"; - - /// - /// Extract all data portal operation methods from a type declaration - /// - public static IReadOnlyList ExtractOperationMethods( - DefinitionExtractionContext context, - ClassDeclarationSyntax classDeclaration) - { - var methods = new List(); - - foreach (var member in classDeclaration.Members) - { - if (member is MethodDeclarationSyntax methodDecl) - { - var operationMethod = TryExtractOperationMethod(context, methodDecl); - if (operationMethod != null) - { - methods.Add(operationMethod); - } - } - } - - return methods; - } - - private static ExtractedOperationMethod? TryExtractOperationMethod( - DefinitionExtractionContext context, - MethodDeclarationSyntax methodDecl) - { - var semanticModel = context.SemanticModel; - var methodSymbol = semanticModel.GetDeclaredSymbol(methodDecl) as IMethodSymbol; - if (methodSymbol == null) - return null; - - var operationAttributes = new List(); - - foreach (var attrData in methodSymbol.GetAttributes()) - { - var attrClassName = attrData.AttributeClass?.ToDisplayString(); - if (attrClassName != null && DataPortalAttributeNames.Contains(attrClassName)) - { - operationAttributes.Add(attrClassName); - } - } - - if (operationAttributes.Count == 0) - return null; - - var result = new ExtractedOperationMethod - { - MethodName = methodSymbol.Name, - IsAsync = IsAsyncMethod(methodSymbol) - }; - - foreach (var attr in operationAttributes) - { - result.OperationAttributeNames.Add(attr); - } - - foreach (var param in methodSymbol.Parameters) - { - var extractedParam = ExtractParameter(param); - if (extractedParam.IsInjected) - { - result.InjectParameters.Add(extractedParam); - } - else - { - result.CriteriaParameters.Add(extractedParam); - } - } - - return result; - } - - private static bool IsAsyncMethod(IMethodSymbol method) - { - if (method.ReturnType is INamedTypeSymbol namedType) - { - var displayName = namedType.ToDisplayString(); - return displayName == "System.Threading.Tasks.Task" || - displayName.StartsWith("System.Threading.Tasks.Task<"); - } - return false; - } - - private static ExtractedOperationParameter ExtractParameter(IParameterSymbol param) - { - var isInjected = false; - var allowNull = false; - - foreach (var attrData in param.GetAttributes()) - { - if (attrData.AttributeClass?.ToDisplayString() == InjectAttributeName) - { - isInjected = true; - - // Check for AllowNull = true - foreach (var namedArg in attrData.NamedArguments) - { - if (namedArg.Key == "AllowNull" && namedArg.Value.Value is bool val) - { - allowNull = val; - } - } - } - } - - return new ExtractedOperationParameter - { - Name = param.Name, - TypeFullName = param.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), - TypeDisplayName = param.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), - TypeMetadataName = GetOperationTypeKey(param.Type), - IsInjected = isInjected, - AllowNull = allowNull - }; - } - - /// - /// Computes a deterministic type key for use in operation names. - /// Arrays: elementType + "Array" (e.g. "Int32Array") - /// Generic types: MetadataName with backtick replaced, then type args (e.g. "List_1_Int32") - /// Simple types: MetadataName (e.g. "Int32", "String") - /// - internal static string GetOperationTypeKey(ITypeSymbol typeSymbol) - { - if (typeSymbol is IArrayTypeSymbol arrayType) - { - return GetOperationTypeKey(arrayType.ElementType) + "Array"; - } - - if (typeSymbol is INamedTypeSymbol namedType && namedType.IsGenericType) - { - var baseName = namedType.MetadataName.Replace('`', '_'); - var typeArgs = string.Join('_', namedType.TypeArguments.Select(GetOperationTypeKey)); - return baseName + "_" + typeArgs; - } - - return typeSymbol.MetadataName; - } - - /// - /// Gets the base operation name from an attribute fully-qualified name. - /// Strips namespace and "Attribute" suffix - /// (e.g. "Csla.FetchAttribute" -> "Fetch") - /// - internal static string GetBaseOperationName(string attributeFullName) - { - var name = attributeFullName; - var dotIndex = name.LastIndexOf('.'); - if (dotIndex >= 0) - name = name.Substring(dotIndex + 1); - if (name.EndsWith("Attribute")) - name = name.Substring(0, name.Length - "Attribute".Length); - return name; - } - } -} diff --git a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Discovery/TypeDefinitionExtractor.cs b/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Discovery/TypeDefinitionExtractor.cs deleted file mode 100644 index 0dd95e4703..0000000000 --- a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Discovery/TypeDefinitionExtractor.cs +++ /dev/null @@ -1,129 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Marimer LLC. All rights reserved. -// Website: https://cslanet.com -// -// Extract the definition of a type for source generation -//----------------------------------------------------------------------- - -using System.Text; -using Csla.Generator.DataPortalInterfaces.CSharp.Extractors; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Csla.Generator.DataPortalInterfaces.CSharp.Discovery -{ - - /// - /// Extract the definition of a type for which data portal interface - /// source generation is required - /// - internal static class TypeDefinitionExtractor - { - - /// - /// Extract the data needed for source generation from the syntax tree - /// - public static ExtractedTypeDefinition ExtractTypeDefinition( - DefinitionExtractionContext extractionContext, - ClassDeclarationSyntax targetTypeDeclaration) - { - var fullyQualifiedNameBuilder = new StringBuilder(); - - var definition = new ExtractedTypeDefinition - { - TypeName = GetTypeName(targetTypeDeclaration), - TypeParameters = GetTypeParameters(targetTypeDeclaration), - Namespace = GetNamespace(targetTypeDeclaration), - Scope = GetScopeDefinition(targetTypeDeclaration), - IsPartial = targetTypeDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword) - }; - - foreach (var containerDefinition in ContainerDefinitionsExtractor.GetContainerDefinitions(extractionContext, targetTypeDeclaration)) - { - definition.ContainerDefinitions.Add(containerDefinition); - fullyQualifiedNameBuilder.Append(containerDefinition.Name).Append('.'); - } - - foreach (var operationMethod in OperationMethodExtractor.ExtractOperationMethods(extractionContext, targetTypeDeclaration)) - { - definition.OperationMethods.Add(operationMethod); - } - - fullyQualifiedNameBuilder.Append(definition.TypeName); - definition.FullyQualifiedName = fullyQualifiedNameBuilder.ToString(); - - return definition; - } - - private static string GetNamespace(TypeDeclarationSyntax targetTypeDeclaration) - { - string nameSpace = string.Empty; - var potentialNamespaceParent = targetTypeDeclaration.Parent; - - while (potentialNamespaceParent != null && - potentialNamespaceParent is not NamespaceDeclarationSyntax && - potentialNamespaceParent is not FileScopedNamespaceDeclarationSyntax) - { - potentialNamespaceParent = potentialNamespaceParent.Parent; - } - - if (potentialNamespaceParent is BaseNamespaceDeclarationSyntax namespaceParent) - { - nameSpace = namespaceParent.Name.ToString(); - - while (true) - { - if (namespaceParent.Parent is not NamespaceDeclarationSyntax parent) - { - break; - } - - nameSpace = $"{parent.Name}.{nameSpace}"; - namespaceParent = parent; - } - } - - return nameSpace; - } - - private static string GetScopeDefinition(TypeDeclarationSyntax targetTypeDeclaration) - { - var scopeNameBuilder = new StringBuilder(); - - foreach (var modifier in targetTypeDeclaration.Modifiers) - { - if (modifier.IsKind(SyntaxKind.PublicKeyword) || - modifier.IsKind(SyntaxKind.InternalKeyword) || - modifier.IsKind(SyntaxKind.ProtectedKeyword) || - modifier.IsKind(SyntaxKind.PrivateKeyword)) - { - scopeNameBuilder.Append(modifier.ValueText); - scopeNameBuilder.Append(' '); - } - } - - if (scopeNameBuilder.Length < 1) - { - scopeNameBuilder.Append("internal "); - } - - return scopeNameBuilder.ToString().TrimEnd(); - } - - private static string GetTypeName(TypeDeclarationSyntax targetTypeDeclaration) - { - return targetTypeDeclaration.Identifier.ToString(); - } - - private static string GetTypeParameters(TypeDeclarationSyntax targetTypeDeclaration) - { - if (targetTypeDeclaration.TypeParameterList != null) - { - return targetTypeDeclaration.TypeParameterList.ToString(); - } - return string.Empty; - } - } -} diff --git a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/ExtractedContainerDefinition.cs b/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/ExtractedContainerDefinition.cs deleted file mode 100644 index 6f769bea7f..0000000000 --- a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/ExtractedContainerDefinition.cs +++ /dev/null @@ -1,28 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Marimer LLC. All rights reserved. -// Website: https://cslanet.com -// -// The definition of a container of a type -//----------------------------------------------------------------------- - -namespace Csla.Generator.DataPortalInterfaces.CSharp.Extractors -{ - - /// - /// The definition of a container of a type, extracted from the syntax tree provided by Roslyn - /// - public class ExtractedContainerDefinition - { - - /// - /// The name of the container - /// - public string Name { get; set; } = string.Empty; - - /// - /// The full definition of the container for use in source generation - /// - public string FullDefinition { get; set; } = string.Empty; - } -} diff --git a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/ExtractedOperationMethod.cs b/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/ExtractedOperationMethod.cs deleted file mode 100644 index 3256631030..0000000000 --- a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/ExtractedOperationMethod.cs +++ /dev/null @@ -1,62 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Marimer LLC. All rights reserved. -// Website: https://cslanet.com -// -// Represents a single data portal operation method -//----------------------------------------------------------------------- - -using System.Collections.Generic; -using System.Linq; -using Csla.Generator.DataPortalInterfaces.CSharp.Discovery; - -namespace Csla.Generator.DataPortalInterfaces.CSharp.Extractors -{ - - /// - /// Represents a single data portal operation method extracted - /// from the target type - /// - public class ExtractedOperationMethod - { - - /// - /// The name of the method - /// - public string MethodName { get; set; } = string.Empty; - - /// - /// Whether the method returns Task (async) - /// - public bool IsAsync { get; set; } - - /// - /// The fully qualified attribute type names on this method - /// (e.g. "Csla.CreateAttribute", "Csla.FetchAttribute") - /// - public IList OperationAttributeNames { get; } = new List(); - - /// - /// The criteria (non-injected) parameters - /// - public IList CriteriaParameters { get; } = new List(); - - /// - /// The injected parameters (marked with [Inject]) - /// - public IList InjectParameters { get; } = new List(); - - /// - /// Computes the deterministic operation name for a given attribute. - /// Format: "OperationType" for no criteria, "OperationType__Type1_Type2" for criteria. - /// - public string GetOperationName(string attributeFullName) - { - var baseName = OperationMethodExtractor.GetBaseOperationName(attributeFullName); - if (CriteriaParameters.Count == 0) - return baseName; - var typeKeys = string.Join('_', CriteriaParameters.Select(p => p.TypeMetadataName)); - return baseName + "__" + typeKeys; - } - } -} diff --git a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/ExtractedOperationParameter.cs b/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/ExtractedOperationParameter.cs deleted file mode 100644 index dc7d57fcd3..0000000000 --- a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/ExtractedOperationParameter.cs +++ /dev/null @@ -1,51 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Marimer LLC. All rights reserved. -// Website: https://cslanet.com -// -// Represents a parameter of a data portal operation method -//----------------------------------------------------------------------- - -namespace Csla.Generator.DataPortalInterfaces.CSharp.Extractors -{ - - /// - /// Represents a parameter of a data portal operation method - /// - public class ExtractedOperationParameter - { - - /// - /// The parameter name - /// - public string Name { get; set; } = string.Empty; - - /// - /// The fully qualified type name of the parameter - /// - public string TypeFullName { get; set; } = string.Empty; - - /// - /// The type name suitable for use in generated code - /// (may include global:: prefix) - /// - public string TypeDisplayName { get; set; } = string.Empty; - - /// - /// The type's metadata name used for operation name computation - /// (e.g. "Int32", "String", "List_1_Int32") - /// - public string TypeMetadataName { get; set; } = string.Empty; - - /// - /// Whether this is an injected parameter - /// - public bool IsInjected { get; set; } - - /// - /// For injected parameters, whether null is allowed - /// (maps to [Inject(AllowNull = true)]) - /// - public bool AllowNull { get; set; } - } -} diff --git a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/ExtractedTypeDefinition.cs b/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/ExtractedTypeDefinition.cs deleted file mode 100644 index fee1ca3294..0000000000 --- a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/ExtractedTypeDefinition.cs +++ /dev/null @@ -1,75 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Marimer LLC. All rights reserved. -// Website: https://cslanet.com -// -// The definition of a type with data portal operations -//----------------------------------------------------------------------- - -using System; -using System.Collections.Generic; - -namespace Csla.Generator.DataPortalInterfaces.CSharp.Extractors -{ - - /// - /// The definition of a type with data portal operations, - /// extracted from the syntax tree provided by Roslyn - /// - public class ExtractedTypeDefinition : IEquatable - { - - /// - /// The namespace in which the type resides - /// - public string Namespace { get; set; } = string.Empty; - - /// - /// The scope of the class (e.g. "public", "internal") - /// - public string Scope { get; set; } = "public"; - - /// - /// The name of the type, excluding any namespace - /// - public string TypeName { get; set; } = string.Empty; - - /// - /// The type parameters (e.g. "<T>") or empty string - /// - public string TypeParameters { get; set; } = string.Empty; - - /// - /// The fully qualified name of the type, including namespace - /// - public string FullyQualifiedName { get; set; } = string.Empty; - - /// - /// The container definitions for this type - /// - public IList ContainerDefinitions { get; } = new List(); - - /// - /// The data portal operation methods on this type - /// - public IList OperationMethods { get; } = new List(); - - /// - /// Whether the class is partial - /// - public bool IsPartial { get; set; } - - /// - public bool Equals(ExtractedTypeDefinition other) - { - if (other is null) return false; - return FullyQualifiedName == other.FullyQualifiedName; - } - - /// - public override bool Equals(object? obj) => obj is ExtractedTypeDefinition other && Equals(other); - - /// - public override int GetHashCode() => FullyQualifiedName?.GetHashCode() ?? 0; - } -} diff --git a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/GenerationResults.cs b/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/GenerationResults.cs deleted file mode 100644 index 18edc012ab..0000000000 --- a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/Extractors/GenerationResults.cs +++ /dev/null @@ -1,28 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Marimer LLC. All rights reserved. -// Website: https://cslanet.com -// -// The results of source generation -//----------------------------------------------------------------------- - -namespace Csla.Generator.DataPortalInterfaces.CSharp.Extractors -{ - - /// - /// The results of source generation - /// - public class GenerationResults - { - - /// - /// The fully qualified name of the generated type - /// - public string FullyQualifiedName { get; set; } = string.Empty; - - /// - /// The source code that has been generated by the builder - /// - public string GeneratedSource { get; set; } = string.Empty; - } -} diff --git a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/IncrementalDataPortalInterfaceGenerator.cs b/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/IncrementalDataPortalInterfaceGenerator.cs deleted file mode 100644 index 644809bdc3..0000000000 --- a/Source/Csla.Generators/cs/DataPortalInterfaces/Csla.Generator.DataPortalInterfaces.CSharp/IncrementalDataPortalInterfaceGenerator.cs +++ /dev/null @@ -1,152 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Marimer LLC. All rights reserved. -// Website: https://cslanet.com -// -// Source generator for data portal operation interface implementations -//----------------------------------------------------------------------- - -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Text; -using Csla.Generator.DataPortalInterfaces.CSharp.Discovery; -using Csla.Generator.DataPortalInterfaces.CSharp.Extractors; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; - -namespace Csla.Generator.DataPortalInterfaces.CSharp -{ - - /// - /// Incremental source generator that creates explicit implementations of - /// IDataPortalOperationMapping for business types with data portal operation methods. - /// - [Generator] - public class IncrementalDataPortalInterfaceGenerator : IIncrementalGenerator - { - /// - /// The short attribute names to look for on methods (syntactic check) - /// - private static readonly HashSet AttributeShortNames = new HashSet - { - "Create", "CreateAttribute", - "Fetch", "FetchAttribute", - "Insert", "InsertAttribute", - "Update", "UpdateAttribute", - "Execute", "ExecuteAttribute", - "Delete", "DeleteAttribute", - "DeleteSelf", "DeleteSelfAttribute", - "CreateChild", "CreateChildAttribute", - "FetchChild", "FetchChildAttribute", - "InsertChild", "InsertChildAttribute", - "UpdateChild", "UpdateChildAttribute", - "DeleteSelfChild", "DeleteSelfChildAttribute", - "ExecuteChild", "ExecuteChildAttribute" - }; - - /// - public void Initialize(IncrementalGeneratorInitializationContext context) - { - var classDeclarations = context.SyntaxProvider.CreateSyntaxProvider( - predicate: static (s, _) => IsCandidateClass(s), - transform: static (ctx, _) => TransformCandidate(ctx) - ).Where(static m => m is not null); - - context.RegisterSourceOutput(classDeclarations, static (spc, typeDefinition) => - { - if (!typeDefinition!.IsPartial) - { - // Emit diagnostic for non-partial class - var diagnostic = Diagnostic.Create( - Diagnostics.NonPartialClassWarning, - Location.None, - typeDefinition.TypeName); - spc.ReportDiagnostic(diagnostic); - return; - } - - var builder = new DataPortalInterfaceBuilder(); - var generationResults = builder.BuildPartialTypeDefinition(typeDefinition); - - spc.AddSource( - $"{generationResults.FullyQualifiedName}.DataPortalOperations.g.cs", - SourceText.From(generationResults.GeneratedSource, Encoding.UTF8)); - }); - } - - private static bool IsCandidateClass(SyntaxNode node) - { - if (node is not ClassDeclarationSyntax classDecl) - return false; - - // Skip abstract classes - they can't be directly instantiated by the data portal - if (classDecl.Modifiers.Any(SyntaxKind.AbstractKeyword)) - return false; - - // Check if any method has a data portal operation attribute - foreach (var member in classDecl.Members) - { - if (member is MethodDeclarationSyntax methodDecl) - { - foreach (var attrList in methodDecl.AttributeLists) - { - foreach (var attr in attrList.Attributes) - { - var name = GetAttributeName(attr); - if (AttributeShortNames.Contains(name)) - return true; - } - } - } - } - - return false; - } - - private static string GetAttributeName(AttributeSyntax attr) - { - switch (attr.Name) - { - case SimpleNameSyntax simpleName: - return simpleName.Identifier.Text; - case QualifiedNameSyntax qualifiedName: - return qualifiedName.Right.Identifier.Text; - default: - return attr.Name.ToString(); - } - } - - private static ExtractedTypeDefinition? TransformCandidate(GeneratorSyntaxContext ctx) - { - var classDecl = (ClassDeclarationSyntax)ctx.Node; - var extractionContext = new DefinitionExtractionContext(ctx.SemanticModel); - var typeDefinition = TypeDefinitionExtractor.ExtractTypeDefinition(extractionContext, classDecl); - - // Verify at least one method actually has a valid CSLA attribute (semantic check) - if (typeDefinition.OperationMethods.Count == 0) - return null; - - return typeDefinition; - } - } - - /// - /// Diagnostic descriptors for the data portal interface generator - /// - internal static class Diagnostics - { - /// - /// Warning when data portal operations are found on a non-partial class - /// - public static readonly DiagnosticDescriptor NonPartialClassWarning = new DiagnosticDescriptor( - id: "CSLADP001", - title: "Data portal operation class should be partial", - messageFormat: "Class '{0}' has data portal operation methods but is not partial. The source generator cannot generate the IDataPortalOperationMapping implementation.", - category: "Csla.DataPortal", - defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true); - } -} diff --git a/Source/Csla/BusinessBindingListBase.cs b/Source/Csla/BusinessBindingListBase.cs index 2d1f0d25c4..da6f9bdf22 100644 --- a/Source/Csla/BusinessBindingListBase.cs +++ b/Source/Csla/BusinessBindingListBase.cs @@ -23,7 +23,7 @@ namespace Csla /// Type of the child objects contained in the list. [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] [Serializable] - public abstract class BusinessBindingListBase<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] C> : + public abstract partial class BusinessBindingListBase<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] C> : ExtendedBindingList, IContainsDeletedList, IEditableCollection, IUndoableObject, ICloneable, ISavable, ISavable, IParent, IDataPortalTarget, diff --git a/Source/Csla/BusinessDocumentBase.cs b/Source/Csla/BusinessDocumentBase.cs index e70d2c343f..07e44d60b7 100644 --- a/Source/Csla/BusinessDocumentBase.cs +++ b/Source/Csla/BusinessDocumentBase.cs @@ -31,7 +31,7 @@ namespace Csla /// Type of the business object being defined. /// Type of the child objects contained in the collection. [Serializable] - public abstract class BusinessDocumentBase< + public abstract partial class BusinessDocumentBase< [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] C> : BusinessBase, diff --git a/Source/Csla/BusinessListBase.cs b/Source/Csla/BusinessListBase.cs index df8017ed87..2a56275050 100644 --- a/Source/Csla/BusinessListBase.cs +++ b/Source/Csla/BusinessListBase.cs @@ -28,7 +28,7 @@ namespace Csla [DebuggerStepThrough] #endif [Serializable] - public abstract class BusinessListBase<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] C> : + public abstract partial class BusinessListBase<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] C> : ObservableBindingList, IContainsDeletedList, ISavable, diff --git a/Source/Csla/CommandBase.cs b/Source/Csla/CommandBase.cs index 1c69800578..82942b8f56 100644 --- a/Source/Csla/CommandBase.cs +++ b/Source/Csla/CommandBase.cs @@ -41,7 +41,7 @@ namespace Csla /// /// [Serializable] - public abstract class CommandBase<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]T> : ManagedObjectBase, + public abstract partial class CommandBase<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]T> : ManagedObjectBase, IDataPortalTarget, IManageProperties, ICommandBase diff --git a/Source/Csla/Csla.csproj b/Source/Csla/Csla.csproj index 980cbcbf8a..ee35afed6b 100644 --- a/Source/Csla/Csla.csproj +++ b/Source/Csla/Csla.csproj @@ -115,7 +115,8 @@ - + + True True @@ -127,4 +128,18 @@ + + + $(TargetsForTfmSpecificContentInPackage);CslaPackBundledGenerators + + + + + + + + + + + diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.Attributes.CSharp/CslaIgnorePropertyAttribute.cs b/Source/Csla/CslaIgnorePropertyAttribute.cs similarity index 100% rename from Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.Attributes.CSharp/CslaIgnorePropertyAttribute.cs rename to Source/Csla/CslaIgnorePropertyAttribute.cs diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.Attributes.CSharp/CslaImplementPropertiesAttribute.cs b/Source/Csla/CslaImplementPropertiesAttribute.cs similarity index 100% rename from Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.Attributes.CSharp/CslaImplementPropertiesAttribute.cs rename to Source/Csla/CslaImplementPropertiesAttribute.cs diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.Attributes.CSharp/CslaImplementPropertiesInterfaceAttribute.cs b/Source/Csla/CslaImplementPropertiesInterfaceAttribute.cs similarity index 100% rename from Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.Attributes.CSharp/CslaImplementPropertiesInterfaceAttribute.cs rename to Source/Csla/CslaImplementPropertiesInterfaceAttribute.cs diff --git a/Source/Csla/DynamicBindingListBase.cs b/Source/Csla/DynamicBindingListBase.cs index 5d3410c27d..7ec955cf89 100644 --- a/Source/Csla/DynamicBindingListBase.cs +++ b/Source/Csla/DynamicBindingListBase.cs @@ -41,7 +41,7 @@ namespace Csla /// [Serializable] [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] - public abstract class DynamicBindingListBase<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T> : + public abstract partial class DynamicBindingListBase<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T> : ExtendedBindingList, IParent, Server.IDataPortalTarget, diff --git a/Source/Csla/DynamicListBase.cs b/Source/Csla/DynamicListBase.cs index b7eaace928..a8487db31e 100644 --- a/Source/Csla/DynamicListBase.cs +++ b/Source/Csla/DynamicListBase.cs @@ -42,7 +42,7 @@ namespace Csla /// /// [Serializable] - public abstract class DynamicListBase<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T> : + public abstract partial class DynamicListBase<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T> : #if ANDROID || IOS ExtendedBindingList, #else diff --git a/Source/Csla/NameValueListBase.cs b/Source/Csla/NameValueListBase.cs index 7815f772ca..aeebc3889a 100644 --- a/Source/Csla/NameValueListBase.cs +++ b/Source/Csla/NameValueListBase.cs @@ -25,7 +25,7 @@ namespace Csla /// Type of the values. [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] [Serializable] - public abstract class NameValueListBase<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] K, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] V> : + public abstract partial class NameValueListBase<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] K, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] V> : ReadOnlyBindingList.NameValuePair>, ICloneable, IDataPortalTarget, diff --git a/Source/Csla/ReadOnlyBase.cs b/Source/Csla/ReadOnlyBase.cs index 312c6b2d6a..c4f23dadf0 100644 --- a/Source/Csla/ReadOnlyBase.cs +++ b/Source/Csla/ReadOnlyBase.cs @@ -36,7 +36,7 @@ namespace Csla /// /// Type of the business object. [Serializable] - public abstract class ReadOnlyBase<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T> : BindableBase, + public abstract partial class ReadOnlyBase<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T> : BindableBase, IDataPortalTarget, IManageProperties, IReadOnlyBase, diff --git a/Source/Csla/ReadOnlyBindingListBase.cs b/Source/Csla/ReadOnlyBindingListBase.cs index ae84c39b65..a239118caf 100644 --- a/Source/Csla/ReadOnlyBindingListBase.cs +++ b/Source/Csla/ReadOnlyBindingListBase.cs @@ -22,7 +22,7 @@ namespace Csla [SuppressMessage( "Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] [Serializable] - public abstract class ReadOnlyBindingListBase : + public abstract partial class ReadOnlyBindingListBase : ReadOnlyBindingList, IReadOnlyCollection, ICloneable, IDataPortalTarget, IUseApplicationContext where T : ReadOnlyBindingListBase diff --git a/Source/Csla/ReadOnlyListBase.cs b/Source/Csla/ReadOnlyListBase.cs index 9fba34f375..cae8705c78 100644 --- a/Source/Csla/ReadOnlyListBase.cs +++ b/Source/Csla/ReadOnlyListBase.cs @@ -24,7 +24,7 @@ namespace Csla /// Type of child objects contained in the list. [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] [Serializable] - public abstract class ReadOnlyListBase : + public abstract partial class ReadOnlyListBase : #if ANDROID || IOS Core.ReadOnlyBindingList, #else diff --git a/Source/Csla/Server/DataPortalOperationHelper.cs b/Source/Csla/Server/DataPortalOperationHelper.cs new file mode 100644 index 0000000000..cb280d602c --- /dev/null +++ b/Source/Csla/Server/DataPortalOperationHelper.cs @@ -0,0 +1,86 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// Runtime support for source-generated data portal dispatch +//----------------------------------------------------------------------- + +using System.ComponentModel; +using Csla.Properties; +using Csla.Reflection; +using Microsoft.Extensions.DependencyInjection; + +namespace Csla.Server +{ + /// + /// Runtime support used by source-generated data portal operation + /// dispatch code. Not intended to be called directly. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static class DataPortalOperationHelper + { + /// + /// Throws when a synchronous data portal call on the client + /// tries to invoke an asynchronous operation method, matching + /// the behavior of reflection-based dispatch. + /// + /// Business object declaring the method. + /// True if the client made a synchronous call. + /// Current service provider. + /// Name of the operation method. + public static void ThrowIfAsyncMethodOnSyncClient(object target, bool isSync, IServiceProvider serviceProvider, string methodName) + { + if (!isSync) + return; + + if (serviceProvider?.GetService(typeof(ApplicationContext)) is ApplicationContext applicationContext + && applicationContext.ExecutionLocation == ApplicationContext.ExecutionLocations.Server) + return; + + throw new CallMethodException( + target.GetType().Name + "." + methodName + " " + Resources.MethodCallFailed, + new NotSupportedException(string.Format(Resources.AsyncMethodOnSyncClientNotAllowed, methodName))); + } + + /// + /// Resolves a service for an injected operation method parameter. + /// + /// Current service provider. + /// Type of service. + /// True if the parameter allows a null value. + /// The service is required and not registered. + public static object? GetService(IServiceProvider serviceProvider, Type serviceType, bool allowNull) + { + if (serviceProvider is null) + throw new ArgumentNullException(nameof(serviceProvider)); + + return allowNull + ? serviceProvider.GetService(serviceType) + : serviceProvider.GetRequiredService(serviceType); + } + + /// + /// Resolves a keyed service for an injected operation method parameter. + /// + /// Current service provider. + /// Type of service. + /// Service key. + /// True if the parameter allows a null value. + /// The service is required and not registered, or the provider does not support keyed services. + public static object? GetKeyedService(IServiceProvider serviceProvider, Type serviceType, object? serviceKey, bool allowNull) + { + if (serviceProvider is null) + throw new ArgumentNullException(nameof(serviceProvider)); + + if (allowNull) + { + if (serviceProvider is IKeyedServiceProvider keyedProvider) + return keyedProvider.GetKeyedService(serviceType, serviceKey); + throw new InvalidOperationException("Service provider must implement IKeyedServiceProvider to support keyed services."); + } + + return serviceProvider.GetRequiredKeyedService(serviceType, serviceKey); + } + } +} diff --git a/Source/Csla/buildTransitive/Csla.targets b/Source/Csla/buildTransitive/Csla.targets new file mode 100644 index 0000000000..b8a7814a0c --- /dev/null +++ b/Source/Csla/buildTransitive/Csla.targets @@ -0,0 +1,15 @@ + + + + + <_CslaRetiredGeneratorPackage Include="@(PackageReference)" Condition="'%(Identity)' == 'Csla.Generator.AutoImplementProperties.CSharp'" /> + + + + diff --git a/Source/csla.build.sln b/Source/csla.build.sln index 59604984fd..715961a794 100644 --- a/Source/csla.build.sln +++ b/Source/csla.build.sln @@ -61,18 +61,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Csla.Generator.AutoImplemen EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Csla.Generator.AutoSerialization.CSharp", "Csla.Generators\cs\AutoSerialization\Csla.Generator.AutoSerialization.CSharp\Csla.Generator.AutoSerialization.CSharp.csproj", "{E838D906-6C66-40E7-3604-9CD97AFD47B0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Csla.Generator.AutoImplementProperties.Attributes.CSharp", "Csla.Generators\cs\AutoImplementProperties\Csla.Generator.AutoImplementProperties.Attributes.CSharp\Csla.Generator.AutoImplementProperties.Attributes.CSharp.csproj", "{CD5F6C1E-8D50-5A23-2733-ABB305796A22}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Csla.Generator.AutoSerialization.Attributes.CSharp", "Csla.Generators\cs\AutoSerialization\Csla.Generator.AutoSerialization.Attributes.CSharp\Csla.Generator.AutoSerialization.Attributes.CSharp.csproj", "{801AD1EE-6FA3-486F-9921-72342C0D1D9A}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Csla.Generators", "Csla.Generators", "{4929775F-66F8-19A3-4CDD-3CBFB3383240}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "cs", "cs", "{C6BA87DF-68CB-9DDE-CDC6-9DA753FB50D8}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataPortalInterfaces", "DataPortalInterfaces", "{42F5FFB7-B37F-9894-5E51-AB6A953DA79E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Csla.Generator.DataPortalInterfaces.CSharp", "Csla.Generators\cs\DataPortalInterfaces\Csla.Generator.DataPortalInterfaces.CSharp\Csla.Generator.DataPortalInterfaces.CSharp.csproj", "{0F476655-B155-432F-ADAC-CE9C77881950}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Csla.Channels.Wcf", "Csla.Channels.Wcf\Csla.Channels.Wcf.csproj", "{50A9114F-5D22-43EF-43CF-559234473966}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Csla.Testing", "Csla.Testing\Csla.Testing.csproj", "{5ECC7150-E415-4FD6-99A4-08110DB54EE1}" @@ -3183,206 +3173,6 @@ Global {E838D906-6C66-40E7-3604-9CD97AFD47B0}.Testing|x64.Build.0 = Debug|Any CPU {E838D906-6C66-40E7-3604-9CD97AFD47B0}.Testing|x86.ActiveCfg = Debug|Any CPU {E838D906-6C66-40E7-3604-9CD97AFD47B0}.Testing|x86.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|AnyOS.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|AnyOS.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|Mixed Platforms.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|Mixed Platforms.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|Win32.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|Win32.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|Win64.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|Win64.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|x64.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Ad-Hoc|x86.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|AnyOS.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|AnyOS.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|ARM.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|ARM.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|iPhone.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|Mixed Platforms.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|Mixed Platforms.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|Win32.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|Win32.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|Win64.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|Win64.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|x64.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|x64.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|x86.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.AppStore|x86.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|Any CPU.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|Any CPU.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|AnyOS.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|AnyOS.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|ARM.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|ARM.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|iPhone.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|iPhone.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|iPhoneSimulator.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|Mixed Platforms.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|Mixed Platforms.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|Win32.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|Win32.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|Win64.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|Win64.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|x64.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|x64.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|x86.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.CD_ROM|x86.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|AnyOS.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|AnyOS.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|ARM.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|ARM.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|iPhone.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|Win32.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|Win32.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|Win64.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|Win64.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|x64.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|x64.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|x86.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|x86.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|Any CPU.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|Any CPU.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|AnyOS.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|AnyOS.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|ARM.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|ARM.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|iPhone.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|iPhone.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|iPhoneSimulator.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|Mixed Platforms.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|Mixed Platforms.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|Win32.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|Win32.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|Win64.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|Win64.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|x64.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|x64.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|x86.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.DVD-5|x86.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|Any CPU.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|AnyOS.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|AnyOS.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|ARM.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|ARM.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|iPhone.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|iPhone.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|Win32.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|Win32.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|Win64.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|Win64.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|x64.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|x64.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|x86.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|x86.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|Any CPU.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|Any CPU.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|AnyOS.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|AnyOS.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|ARM.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|ARM.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|iPhone.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|iPhone.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|iPhoneSimulator.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|Mixed Platforms.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|Win32.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|Win32.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|Win64.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|Win64.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|x64.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|x64.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|x86.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|x86.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|Any CPU.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|AnyOS.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|AnyOS.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|ARM.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|ARM.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|iPhone.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|iPhone.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|Win32.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|Win32.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|Win64.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|Win64.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|x64.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|x64.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|x86.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|x86.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|Any CPU.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|Any CPU.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|AnyOS.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|AnyOS.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|ARM.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|ARM.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|iPhone.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|iPhone.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|iPhoneSimulator.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|Mixed Platforms.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|Mixed Platforms.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|Win32.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|Win32.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|Win64.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|Win64.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|x64.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|x64.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|x86.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.SingleImage|x86.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|Any CPU.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|Any CPU.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|AnyOS.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|AnyOS.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|ARM.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|ARM.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|iPhone.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|iPhone.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|iPhoneSimulator.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|Mixed Platforms.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|Mixed Platforms.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|Win32.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|Win32.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|Win64.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|Win64.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|x64.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|x64.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|x86.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|x86.Build.0 = Debug|Any CPU {801AD1EE-6FA3-486F-9921-72342C0D1D9A}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU {801AD1EE-6FA3-486F-9921-72342C0D1D9A}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU {801AD1EE-6FA3-486F-9921-72342C0D1D9A}.Ad-Hoc|AnyOS.ActiveCfg = Debug|Any CPU @@ -3583,206 +3373,6 @@ Global {801AD1EE-6FA3-486F-9921-72342C0D1D9A}.Testing|x64.Build.0 = Debug|Any CPU {801AD1EE-6FA3-486F-9921-72342C0D1D9A}.Testing|x86.ActiveCfg = Debug|Any CPU {801AD1EE-6FA3-486F-9921-72342C0D1D9A}.Testing|x86.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|AnyOS.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|AnyOS.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|Mixed Platforms.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|Mixed Platforms.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|Win32.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|Win32.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|Win64.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|Win64.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|x64.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Ad-Hoc|x86.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|Any CPU.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|AnyOS.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|AnyOS.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|ARM.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|ARM.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|iPhone.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|iPhone.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|Mixed Platforms.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|Mixed Platforms.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|Win32.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|Win32.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|Win64.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|Win64.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|x64.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|x64.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|x86.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.AppStore|x86.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|Any CPU.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|Any CPU.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|AnyOS.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|AnyOS.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|ARM.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|ARM.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|iPhone.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|iPhone.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|iPhoneSimulator.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|Mixed Platforms.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|Mixed Platforms.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|Win32.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|Win32.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|Win64.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|Win64.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|x64.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|x64.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|x86.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.CD_ROM|x86.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|AnyOS.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|AnyOS.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|ARM.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|ARM.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|iPhone.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|Win32.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|Win32.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|Win64.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|Win64.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|x64.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|x64.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|x86.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Debug|x86.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|Any CPU.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|Any CPU.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|AnyOS.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|AnyOS.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|ARM.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|ARM.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|iPhone.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|iPhone.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|iPhoneSimulator.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|Mixed Platforms.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|Mixed Platforms.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|Win32.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|Win32.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|Win64.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|Win64.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|x64.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|x64.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|x86.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.DVD-5|x86.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|Any CPU.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|AnyOS.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|AnyOS.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|ARM.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|ARM.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|iPhone.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|iPhone.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|Win32.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|Win32.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|Win64.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|Win64.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|x64.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|x64.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|x86.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Debug|x86.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|Any CPU.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|Any CPU.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|AnyOS.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|AnyOS.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|ARM.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|ARM.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|iPhone.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|iPhone.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|iPhoneSimulator.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|Mixed Platforms.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|Mixed Platforms.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|Win32.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|Win32.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|Win64.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|Win64.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|x64.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|x64.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|x86.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Mono Release|x86.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|Any CPU.Build.0 = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|AnyOS.ActiveCfg = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|AnyOS.Build.0 = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|ARM.ActiveCfg = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|ARM.Build.0 = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|iPhone.ActiveCfg = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|iPhone.Build.0 = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|Win32.ActiveCfg = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|Win32.Build.0 = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|Win64.ActiveCfg = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|Win64.Build.0 = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|x64.ActiveCfg = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|x64.Build.0 = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|x86.ActiveCfg = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Release|x86.Build.0 = Release|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|Any CPU.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|Any CPU.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|AnyOS.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|AnyOS.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|ARM.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|ARM.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|iPhone.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|iPhone.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|iPhoneSimulator.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|Mixed Platforms.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|Mixed Platforms.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|Win32.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|Win32.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|Win64.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|Win64.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|x64.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|x64.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|x86.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.SingleImage|x86.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|Any CPU.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|Any CPU.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|AnyOS.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|AnyOS.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|ARM.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|ARM.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|iPhone.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|iPhone.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|iPhoneSimulator.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|Mixed Platforms.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|Mixed Platforms.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|Win32.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|Win32.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|Win64.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|Win64.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|x64.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|x64.Build.0 = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|x86.ActiveCfg = Debug|Any CPU - {0F476655-B155-432F-ADAC-CE9C77881950}.Testing|x86.Build.0 = Debug|Any CPU {50A9114F-5D22-43EF-43CF-559234473966}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU {50A9114F-5D22-43EF-43CF-559234473966}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU {50A9114F-5D22-43EF-43CF-559234473966}.Ad-Hoc|AnyOS.ActiveCfg = Release|Any CPU @@ -4406,11 +3996,7 @@ Global {55127B94-9D7B-4AB6-A2CD-2E66B91602E3} = {3C1B872F-4DDC-4758-82CE-A052E5909204} {3F0193BB-6B1D-E2A3-ABEB-538D3494C837} = {8736485A-15E4-42A0-91CC-AE785DAD5AB2} {E838D906-6C66-40E7-3604-9CD97AFD47B0} = {8736485A-15E4-42A0-91CC-AE785DAD5AB2} - {CD5F6C1E-8D50-5A23-2733-ABB305796A22} = {8736485A-15E4-42A0-91CC-AE785DAD5AB2} {801AD1EE-6FA3-486F-9921-72342C0D1D9A} = {8736485A-15E4-42A0-91CC-AE785DAD5AB2} - {C6BA87DF-68CB-9DDE-CDC6-9DA753FB50D8} = {4929775F-66F8-19A3-4CDD-3CBFB3383240} - {42F5FFB7-B37F-9894-5E51-AB6A953DA79E} = {C6BA87DF-68CB-9DDE-CDC6-9DA753FB50D8} - {0F476655-B155-432F-ADAC-CE9C77881950} = {42F5FFB7-B37F-9894-5E51-AB6A953DA79E} {50A9114F-5D22-43EF-43CF-559234473966} = {580424DE-C982-4E80-A948-EF42FC3AB110} {5ECC7150-E415-4FD6-99A4-08110DB54EE1} = {85799E16-9B3B-4BD5-98D6-238E31291134} {F6080E51-675F-47A8-B125-55DCF66359F8} = {A26EBA9F-1777-46F2-994A-4C4D3055873D} diff --git a/Source/csla.test.sln b/Source/csla.test.sln index 8376e00bd1..b71fe1bbc1 100644 --- a/Source/csla.test.sln +++ b/Source/csla.test.sln @@ -71,8 +71,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Csla.Generator.AutoImplemen EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Csla.Generator.AutoSerialization.CSharp", "Csla.Generators\cs\AutoSerialization\Csla.Generator.AutoSerialization.CSharp\Csla.Generator.AutoSerialization.CSharp.csproj", "{E838D906-6C66-40E7-3604-9CD97AFD47B0}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Csla.Generator.AutoImplementProperties.Attributes.CSharp", "Csla.Generators\cs\AutoImplementProperties\Csla.Generator.AutoImplementProperties.Attributes.CSharp\Csla.Generator.AutoImplementProperties.Attributes.CSharp.csproj", "{CD5F6C1E-8D50-5A23-2733-ABB305796A22}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Csla.Generator.AutoSerialization.Attributes.CSharp", "Csla.Generators\cs\AutoSerialization\Csla.Generator.AutoSerialization.Attributes.CSharp\Csla.Generator.AutoSerialization.Attributes.CSharp.csproj", "{11AF2F64-BB8B-A5F7-DC5F-5BFD15CF9B2E}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Csla.Windows.Tests", "tests\Csla.Windows.Tests\Csla.Windows.Tests.csproj", "{4A3CB14D-787F-4091-9BB3-53BB14D79C2A}" @@ -85,18 +83,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Csla.Generator.AutoSerializ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Csla.Generator.AutoSerialization.CSharp.Tests", "tests\Csla.Generator.AutoSerialization.CSharp.Tests\Csla.Generator.AutoSerialization.CSharp.Tests.csproj", "{32BD631E-431B-95B5-2E49-160A06C1F2EB}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Csla.Generators", "Csla.Generators", "{4929775F-66F8-19A3-4CDD-3CBFB3383240}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "cs", "cs", "{C6BA87DF-68CB-9DDE-CDC6-9DA753FB50D8}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataPortalInterfaces", "DataPortalInterfaces", "{42F5FFB7-B37F-9894-5E51-AB6A953DA79E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Csla.Generator.DataPortalInterfaces.CSharp", "Csla.Generators\cs\DataPortalInterfaces\Csla.Generator.DataPortalInterfaces.CSharp\Csla.Generator.DataPortalInterfaces.CSharp.csproj", "{D1ED800F-5D7F-43FA-BCAC-A0B244724103}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Csla.Generator.DataPortalInterfaces.CSharp.Tests", "tests\Csla.Generator.DataPortalInterfaces.CSharp.Tests\Csla.Generator.DataPortalInterfaces.CSharp.Tests.csproj", "{3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Csla.Channels.Wcf", "Csla.Channels.Wcf\Csla.Channels.Wcf.csproj", "{50A9114F-5D22-43EF-43CF-559234473966}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Csla.Testing", "Csla.Testing\Csla.Testing.csproj", "{45D65B40-1387-4D6F-AFA7-E8AE40E442FC}" @@ -1129,46 +1117,6 @@ Global {E838D906-6C66-40E7-3604-9CD97AFD47B0}.Testing|x64.Build.0 = Debug|Any CPU {E838D906-6C66-40E7-3604-9CD97AFD47B0}.Testing|x86.ActiveCfg = Debug|Any CPU {E838D906-6C66-40E7-3604-9CD97AFD47B0}.Testing|x86.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|ARM.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|ARM.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|x64.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|x64.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|x86.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Debug|x86.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|Any CPU.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|ARM.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|ARM.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|x64.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|x64.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|x86.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Debug|x86.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|Any CPU.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|Any CPU.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|ARM.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|ARM.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|x64.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|x64.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|x86.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Mono Release|x86.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|Any CPU.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|ARM.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|ARM.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|x64.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|x64.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|x86.ActiveCfg = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Release|x86.Build.0 = Release|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|Any CPU.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|Any CPU.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|ARM.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|ARM.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|x64.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|x64.Build.0 = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|x86.ActiveCfg = Debug|Any CPU - {CD5F6C1E-8D50-5A23-2733-ABB305796A22}.Testing|x86.Build.0 = Debug|Any CPU {11AF2F64-BB8B-A5F7-DC5F-5BFD15CF9B2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {11AF2F64-BB8B-A5F7-DC5F-5BFD15CF9B2E}.Debug|Any CPU.Build.0 = Debug|Any CPU {11AF2F64-BB8B-A5F7-DC5F-5BFD15CF9B2E}.Debug|ARM.ActiveCfg = Debug|Any CPU @@ -1409,86 +1357,6 @@ Global {32BD631E-431B-95B5-2E49-160A06C1F2EB}.Testing|x64.Build.0 = Debug|Any CPU {32BD631E-431B-95B5-2E49-160A06C1F2EB}.Testing|x86.ActiveCfg = Debug|Any CPU {32BD631E-431B-95B5-2E49-160A06C1F2EB}.Testing|x86.Build.0 = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Debug|ARM.ActiveCfg = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Debug|ARM.Build.0 = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Debug|x64.ActiveCfg = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Debug|x64.Build.0 = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Debug|x86.ActiveCfg = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Debug|x86.Build.0 = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Mono Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Mono Debug|Any CPU.Build.0 = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Mono Debug|ARM.ActiveCfg = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Mono Debug|ARM.Build.0 = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Mono Debug|x64.ActiveCfg = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Mono Debug|x64.Build.0 = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Mono Debug|x86.ActiveCfg = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Mono Debug|x86.Build.0 = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Mono Release|Any CPU.ActiveCfg = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Mono Release|Any CPU.Build.0 = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Mono Release|ARM.ActiveCfg = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Mono Release|ARM.Build.0 = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Mono Release|x64.ActiveCfg = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Mono Release|x64.Build.0 = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Mono Release|x86.ActiveCfg = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Mono Release|x86.Build.0 = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Release|Any CPU.Build.0 = Release|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Release|ARM.ActiveCfg = Release|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Release|ARM.Build.0 = Release|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Release|x64.ActiveCfg = Release|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Release|x64.Build.0 = Release|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Release|x86.ActiveCfg = Release|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Release|x86.Build.0 = Release|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Testing|Any CPU.ActiveCfg = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Testing|Any CPU.Build.0 = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Testing|ARM.ActiveCfg = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Testing|ARM.Build.0 = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Testing|x64.ActiveCfg = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Testing|x64.Build.0 = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Testing|x86.ActiveCfg = Debug|Any CPU - {D1ED800F-5D7F-43FA-BCAC-A0B244724103}.Testing|x86.Build.0 = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Debug|ARM.ActiveCfg = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Debug|ARM.Build.0 = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Debug|x64.ActiveCfg = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Debug|x64.Build.0 = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Debug|x86.ActiveCfg = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Debug|x86.Build.0 = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Mono Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Mono Debug|Any CPU.Build.0 = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Mono Debug|ARM.ActiveCfg = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Mono Debug|ARM.Build.0 = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Mono Debug|x64.ActiveCfg = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Mono Debug|x64.Build.0 = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Mono Debug|x86.ActiveCfg = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Mono Debug|x86.Build.0 = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Mono Release|Any CPU.ActiveCfg = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Mono Release|Any CPU.Build.0 = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Mono Release|ARM.ActiveCfg = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Mono Release|ARM.Build.0 = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Mono Release|x64.ActiveCfg = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Mono Release|x64.Build.0 = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Mono Release|x86.ActiveCfg = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Mono Release|x86.Build.0 = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Release|Any CPU.Build.0 = Release|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Release|ARM.ActiveCfg = Release|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Release|ARM.Build.0 = Release|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Release|x64.ActiveCfg = Release|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Release|x64.Build.0 = Release|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Release|x86.ActiveCfg = Release|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Release|x86.Build.0 = Release|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Testing|Any CPU.ActiveCfg = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Testing|Any CPU.Build.0 = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Testing|ARM.ActiveCfg = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Testing|ARM.Build.0 = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Testing|x64.ActiveCfg = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Testing|x64.Build.0 = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Testing|x86.ActiveCfg = Debug|Any CPU - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50}.Testing|x86.Build.0 = Debug|Any CPU {50A9114F-5D22-43EF-43CF-559234473966}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {50A9114F-5D22-43EF-43CF-559234473966}.Debug|Any CPU.Build.0 = Debug|Any CPU {50A9114F-5D22-43EF-43CF-559234473966}.Debug|ARM.ActiveCfg = Debug|Any CPU @@ -1671,16 +1539,11 @@ Global {9D8A32E2-E82E-425C-A8EA-AC2BC17CCA0D} = {01AA1A57-727E-4665-93FF-6E8BDD316ADE} {3F0193BB-6B1D-E2A3-ABEB-538D3494C837} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {E838D906-6C66-40E7-3604-9CD97AFD47B0} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} - {CD5F6C1E-8D50-5A23-2733-ABB305796A22} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {11AF2F64-BB8B-A5F7-DC5F-5BFD15CF9B2E} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {D70E04EB-B295-AF38-818C-3972657B18E8} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {916A3B6C-8EFA-DAE5-09A8-7E38A03F033C} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {3B24B172-0CD6-FD10-A771-1E90C7E69116} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {32BD631E-431B-95B5-2E49-160A06C1F2EB} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} - {C6BA87DF-68CB-9DDE-CDC6-9DA753FB50D8} = {4929775F-66F8-19A3-4CDD-3CBFB3383240} - {42F5FFB7-B37F-9894-5E51-AB6A953DA79E} = {C6BA87DF-68CB-9DDE-CDC6-9DA753FB50D8} - {D1ED800F-5D7F-43FA-BCAC-A0B244724103} = {42F5FFB7-B37F-9894-5E51-AB6A953DA79E} - {3CA1B447-AD2D-4ED1-B005-0E9968ED9C50} = {0AB3BF05-4346-4AA6-1389-037BE0695223} {50A9114F-5D22-43EF-43CF-559234473966} = {01AA1A57-727E-4665-93FF-6E8BDD316ADE} {45D65B40-1387-4D6F-AFA7-E8AE40E442FC} = {01AA1A57-727E-4665-93FF-6E8BDD316ADE} {8276B605-DBCF-484A-9603-BA1A6201A9F3} = {0AB3BF05-4346-4AA6-1389-037BE0695223} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.TestObjects/Csla.Generator.AutoImplementProperties.TestObjects.csproj b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.TestObjects/Csla.Generator.AutoImplementProperties.TestObjects.csproj index a2e11603e6..4483b810f5 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.TestObjects/Csla.Generator.AutoImplementProperties.TestObjects.csproj +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.TestObjects/Csla.Generator.AutoImplementProperties.TestObjects.csproj @@ -7,7 +7,6 @@ - diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Csla.Generator.AutoImplementProperties.CSharp.Tests.csproj b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Csla.Generator.AutoImplementProperties.CSharp.Tests.csproj index 0f761a40d6..d352f198e6 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Csla.Generator.AutoImplementProperties.CSharp.Tests.csproj +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Csla.Generator.AutoImplementProperties.CSharp.Tests.csproj @@ -30,7 +30,7 @@ - + diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsGeneratorTests.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsGeneratorTests.cs new file mode 100644 index 0000000000..7b718eb975 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsGeneratorTests.cs @@ -0,0 +1,660 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +//----------------------------------------------------------------------- +using Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations; + +namespace Csla.Generator.AutoImplementProperties.CSharp.Tests.DataPortalOperations +{ + [TestClass] + public class DataPortalOperationsGeneratorTests : VerifyBase + { + [TestMethod("Single Create operation with no parameters")] + public async Task SingleCreateNoParams() + { + var source = """ + using Csla; + + namespace TestApp + { + public partial class PersonEdit : Csla.BusinessBase + { + [Create] + private void Create() { } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Single Fetch operation with one criteria parameter")] + public async Task SingleFetchWithCriteria() + { + var source = """ + using Csla; + + namespace TestApp + { + public partial class PersonEdit : Csla.BusinessBase + { + [Fetch] + private void Fetch(int id) { } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Operation method with Inject parameter")] + public async Task OperationWithInjectParam() + { + var source = """ + using Csla; + using System.Threading.Tasks; + + namespace TestApp + { + public interface IDal { } + + public partial class PersonEdit : Csla.BusinessBase + { + [Insert] + private async Task Insert([Inject] IDal dal) { } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Operation method with Inject AllowNull parameter")] + public async Task OperationWithInjectAllowNull() + { + var source = """ + using Csla; + using System.Threading.Tasks; + + namespace TestApp + { + public interface ILogger { } + + public partial class PersonEdit : Csla.BusinessBase + { + [Fetch] + private async Task Fetch(int id, [Inject(AllowNull = true)] ILogger? logger) { } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Multiple overloads of same Fetch operation")] + public async Task MultipleOverloads() + { + var source = """ + using Csla; + + namespace TestApp + { + public partial class PersonEdit : Csla.BusinessBase + { + [Fetch] + private void Fetch(string name) { } + + [Fetch] + private void Fetch(int id) { } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Method with both root and child attributes")] + public async Task RootAndChildAttributes() + { + var source = """ + using Csla; + + namespace TestApp + { + public partial class PersonEdit : Csla.BusinessBase + { + [Create] + [CreateChild] + private void Create() { } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Async method returning Task")] + public async Task AsyncMethod() + { + var source = """ + using Csla; + using System.Threading.Tasks; + + namespace TestApp + { + public partial class PersonEdit : Csla.BusinessBase + { + [Fetch] + private async Task Fetch(int id) { await Task.CompletedTask; } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("File-scoped namespace")] + public async Task FileScopedNamespace() + { + var source = """ + using Csla; + + namespace TestApp; + + public partial class PersonEdit : Csla.BusinessBase + { + [Create] + private void Create() { } + + [Fetch] + private void Fetch(int id) { } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Complete CRUD operations")] + public async Task CompleteCrud() + { + var source = """ + using Csla; + using System.Threading.Tasks; + + namespace TestApp + { + public interface IDal { } + + public partial class PersonEdit : Csla.BusinessBase + { + [Create] + private void Create() { } + + [Fetch] + private void Fetch(int id) { } + + [Insert] + private async Task Insert([Inject] IDal dal) { await Task.CompletedTask; } + + [Update] + private async Task Update([Inject] IDal dal) { await Task.CompletedTask; } + + [DeleteSelf] + private async Task DeleteSelf([Inject] IDal dal) { await Task.CompletedTask; } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Delete operation with criteria")] + public async Task DeleteWithCriteria() + { + var source = """ + using Csla; + + namespace TestApp + { + public partial class PersonEdit : Csla.BusinessBase + { + [Delete] + private void Delete(int id) { } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Child operations")] + public async Task ChildOperations() + { + var source = """ + using Csla; + using System.Threading.Tasks; + + namespace TestApp + { + public interface IDal { } + + public partial class LineItem : Csla.BusinessBase + { + [CreateChild] + private void CreateChild() { } + + [FetchChild] + private void FetchChild(int id) { } + + [InsertChild] + private async Task InsertChild([Inject] IDal dal) { await Task.CompletedTask; } + + [UpdateChild] + private async Task UpdateChild([Inject] IDal dal) { await Task.CompletedTask; } + + [DeleteSelfChild] + private async Task DeleteSelfChild([Inject] IDal dal) { await Task.CompletedTask; } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Execute operation on command object")] + public async Task ExecuteCommand() + { + var source = """ + using Csla; + + namespace TestApp + { + public partial class MyCommand : Csla.CommandBase + { + [Execute] + private void Execute() { } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Nested class with data portal operations")] + public async Task NestedClass() + { + var source = """ + using Csla; + + namespace TestApp + { + public partial class Outer + { + public partial class PersonEdit : Csla.BusinessBase + { + [Fetch] + private void Fetch(int id) { } + } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Inject parameter declared before criteria keeps parameter order")] + public async Task InjectBeforeCriteria() + { + var source = """ + using Csla; + + namespace TestApp + { + public interface IDal { } + + public partial class PersonEdit : Csla.BusinessBase + { + [Fetch] + private void Fetch([Inject] IDal dal, int id, [Inject(AllowNull = true)] IDal? backup, string name) { } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Array, nullable, generic and tuple criteria")] + public async Task ArrayNullableGenericCriteria() + { + var source = """ + using Csla; + using System.Collections.Generic; + + namespace TestApp + { + public partial class PersonEdit : Csla.BusinessBase + { + [Fetch] + private void Fetch(int[] ids) { } + + [Fetch] + private void Fetch(int? id, string? name) { } + + [Fetch] + private void Fetch(List ids) { } + + [Fetch] + private void Fetch((int Id, string Name) key) { } + + [Fetch] + private void Fetch(Dictionary>[] map) { } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Partial class with operation methods spread across two files")] + public async Task PartialClassAcrossFiles() + { + var source1 = """ + using Csla; + + namespace TestApp + { + public partial class PersonEdit : Csla.BusinessBase + { + [Create] + private void Create() { } + } + } + """; + + var source2 = """ + using Csla; + + namespace TestApp + { + public partial class PersonEdit + { + [Fetch] + private void Fetch(int id) { } + } + } + """; + + await TestHelperVerify(source1, source2); + } + + [TestMethod("Abstract class gets the operations interface only")] + public async Task AbstractClass() + { + var source = """ + using Csla; + + namespace TestApp + { + public abstract partial class PersonBase : Csla.BusinessBase + where T : PersonBase + { + [Fetch] + private void Fetch(int id) { } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Derived class hides the operations interface of a base class in the same assembly")] + public async Task DerivedFromBaseWithOperations() + { + var source = """ + using Csla; + + namespace TestApp + { + public abstract partial class PersonBase : Csla.BusinessBase + where T : PersonBase + { + [Fetch] + private void Fetch(int id) { } + } + + public partial class PersonEdit : PersonBase + { + [Fetch] + private void Fetch(string name) { } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Generic business object excludes type parameter criteria from name dispatch")] + public async Task GenericBusinessObject() + { + var source = """ + using Csla; + + namespace TestApp + { + public partial class Lookup : Csla.ReadOnlyBase> + { + [Fetch] + private void Fetch(TKey key) { } + + [Fetch] + private void Fetch(int id) { } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("RunLocal and ValueTask operation methods")] + public async Task RunLocalAndValueTask() + { + var source = """ + using Csla; + using System.Threading.Tasks; + + namespace TestApp + { + public partial class PersonEdit : Csla.BusinessBase + { + [RunLocal] + [Create] + private void Create() { } + + [Fetch] + private async ValueTask Fetch(int id) { await Task.Yield(); } + + [Fetch] + private ValueTask Fetch(string name) => new ValueTask(0); + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Keyed inject parameters")] + public async Task KeyedInject() + { + var source = """ + using Csla; + + namespace TestApp + { + public interface IDal { } + + public enum DalKind { Primary, Secondary } + + public partial class PersonEdit : Csla.BusinessBase + { + [Fetch] + private void Fetch(int id, [Inject(Key = "primary")] IDal dal, [Inject(Key = DalKind.Secondary, AllowNull = true)] IDal? secondary) { } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Overloads with the same operation name report a diagnostic")] + public async Task DuplicateOperationName() + { + var source = """ + using Csla; + + namespace TestApp + { + public interface IDal { } + + public partial class PersonEdit : Csla.BusinessBase + { + [Fetch] + private void Fetch(int id) { } + + [Fetch] + private void Fetch(int id, [Inject] IDal dal) { } + + [Fetch] + private void Load(int id) { } + } + } + """; + + await DataPortalOperationsTestHelper.VerifyWithDiagnostics(source); + } + + [TestMethod("Overloads with the same criteria count require exact types in type-based dispatch")] + public async Task AmbiguousOverloads() + { + var source = """ + using Csla; + + namespace TestApp + { + public class Criteria { } + + public partial class PersonEdit : Csla.BusinessBase + { + [Fetch] + private void Fetch(object value) { } + + [Fetch] + private void Fetch(Criteria criteria) { } + + [Fetch] + private void Fetch(int id) { } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Params object array and keyword parameter names")] + public async Task ParamsArrayAndKeywordNames() + { + var source = """ + using Csla; + + namespace TestApp + { + public partial class PersonEdit : Csla.BusinessBase + { + [Fetch] + private void Fetch(params object[] values) { } + + [Create] + private void Create(int @class, string @event = "x") { } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Non-partial class generates nothing")] + public async Task NonPartialClass() + { + var source = """ + using Csla; + + namespace TestApp + { + public class PersonEdit : Csla.BusinessBase + { + [Fetch] + private void Fetch(int id) { } + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Class that implements the named mapping itself only gets the operations interface and type mapping")] + public async Task ExistingNamedMapping() + { + var source = """ + using Csla; + using System; + using System.Threading.Tasks; + + namespace TestApp + { + public partial class PersonEdit : Csla.BusinessBase, Csla.Server.IDataPortalOperationNamedMapping + { + [Fetch] + private void Fetch(int id) { } + + public Task InvokeNamedOperationAsync(string operationName, bool isSync, object?[]? criteria, IServiceProvider serviceProvider) + => Task.CompletedTask; + } + } + """; + + await TestHelperVerify(source); + } + + [TestMethod("Pipeline stages are cached when the compilation does not change")] + public void StageCaching() + { + var source = """ + using Csla; + using System.Threading.Tasks; + + namespace TestApp + { + public interface IDal { } + + public partial class PersonEdit : Csla.BusinessBase + { + [Create] + private void Create() { } + + [Fetch] + [FetchChild] + private async Task Fetch(int id, [Inject] IDal dal) { await Task.CompletedTask; } + } + } + """; + + StageCachingTester.VerifyStageCaching(source, typeof(TrackingNames)); + } + + private static async Task TestHelperVerify(string source, params string[]? additionalSources) + { + await DataPortalOperationsTestHelper.Verify(source, additionalSources); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsTestHelper.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsTestHelper.cs new file mode 100644 index 0000000000..9521d2259b --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsTestHelper.cs @@ -0,0 +1,98 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +//----------------------------------------------------------------------- +using FluentAssertions; +using FluentAssertions.Execution; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +namespace Csla.Generator.AutoImplementProperties.CSharp.Tests.DataPortalOperations +{ + public static class DataPortalOperationsTestHelper where T : IIncrementalGenerator, new() + { + /// + /// Runs the generator and verifies the output against snapshots, + /// asserting the generator reports no diagnostics and the resulting + /// compilation has no errors or warnings. + /// + public static Task Verify(string source, IEnumerable? additionalSources = null) + { + var (driver, outputCompilation, diagnostics) = Run(source, additionalSources); + + using (new AssertionScope()) + { + outputCompilation.GetDiagnostics() + .Where(d => d.Severity == DiagnosticSeverity.Error + || (d.Severity == DiagnosticSeverity.Warning && IsGenerated(d))) + .Should().BeEmpty(); + diagnostics.Should().BeEmpty(); + } + + return Verifier.Verify(driver).UseDirectory("Snapshots"); + } + + /// + /// Runs the generator and verifies the output, including any generator + /// diagnostics, against snapshots. The resulting compilation must have no errors. + /// + public static Task VerifyWithDiagnostics(string source, IEnumerable? additionalSources = null) + { + var (driver, outputCompilation, _) = Run(source, additionalSources); + + outputCompilation.GetDiagnostics() + .Where(d => d.Severity == DiagnosticSeverity.Error) + .Should().BeEmpty(); + + return Verifier.Verify(driver).UseDirectory("Snapshots"); + } + + /// + /// Creates the compilation and generator driver used by the tests. + /// + public static (GeneratorDriver Driver, CSharpCompilation Compilation) Setup(string source, IEnumerable? additionalSources = null, bool trackSteps = false) + { + var syntaxTrees = new List + { + CSharpSyntaxTree.ParseText(source, path: "Source0.cs") + }; + + var index = 1; + foreach (var additionalSource in additionalSources ?? []) + syntaxTrees.Add(CSharpSyntaxTree.ParseText(additionalSource, path: $"Source{index++}.cs")); + + var references = AppDomain.CurrentDomain.GetAssemblies() + .Where(a => !a.IsDynamic && !string.IsNullOrWhiteSpace(a.Location)) + .Select(a => MetadataReference.CreateFromFile(a.Location)) + .Concat([ + MetadataReference.CreateFromFile(typeof(FetchAttribute).Assembly.Location) + ]); + + var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) + .WithNullableContextOptions(NullableContextOptions.Enable) + .WithSpecificDiagnosticOptions(new Dictionary { { "CS8019", ReportDiagnostic.Suppress } }); + + var compilation = CSharpCompilation.Create( + assemblyName: "Tests", + syntaxTrees: syntaxTrees, + references: references, + options: compilationOptions); + + var driverOptions = new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: trackSteps); + GeneratorDriver driver = CSharpGeneratorDriver.Create([new T().AsSourceGenerator()], driverOptions: driverOptions); + return (driver, compilation); + } + + private static bool IsGenerated(Diagnostic diagnostic) + => diagnostic.Location.SourceTree?.FilePath.EndsWith(".g.cs", StringComparison.Ordinal) == true; + + private static (GeneratorDriver Driver, Compilation OutputCompilation, System.Collections.Immutable.ImmutableArray Diagnostics) Run(string source, IEnumerable? additionalSources) + { + var (driver, compilation) = Setup(source, additionalSources); + driver = driver.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var diagnostics); + return (driver, outputCompilation, diagnostics); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AbstractClass#TestApp.PersonBase_1.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AbstractClass#TestApp.PersonBase_1.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..445348c475 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AbstractClass#TestApp.PersonBase_1.DataPortalOperations.g.verified.cs @@ -0,0 +1,22 @@ +//HintName: TestApp.PersonBase_1.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonBase : global::TestApp.PersonBase.IDataPortalOperations + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Fetch__Int32(int id); + } + + void IDataPortalOperations.Fetch__Int32(int id) => Fetch(id); + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..c9adc2bcae --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,82 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Fetch__Object(object value); + void Fetch__Criteria(global::TestApp.Criteria criteria); + void Fetch__Int32(int id); + } + + void IDataPortalOperations.Fetch__Object(object value) => Fetch(value); + void IDataPortalOperations.Fetch__Criteria(global::TestApp.Criteria criteria) => Fetch(criteria); + void IDataPortalOperations.Fetch__Int32(int id) => Fetch(id); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is object __c0p0 && __c0p0.GetType() == typeof(object)) + { + __operations.Fetch__Object(__c0p0); + return; + } + if (criteria is { Length: 1 } && criteria[0] is global::TestApp.Criteria __c1p0 && __c1p0.GetType() == typeof(global::TestApp.Criteria)) + { + __operations.Fetch__Criteria(__c1p0); + return; + } + if (criteria is { Length: 1 } && criteria[0] is int __c2p0) + { + __operations.Fetch__Int32(__c2p0); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Fetch__Object": + if (criteria is { Length: 1 } && (criteria[0] is null || criteria[0] is object)) + { + __operations.Fetch__Object((object)criteria[0]!); + return; + } + break; + case "Fetch__Criteria": + if (criteria is { Length: 1 } && (criteria[0] is null || criteria[0] is global::TestApp.Criteria)) + { + __operations.Fetch__Criteria((global::TestApp.Criteria)criteria[0]!); + return; + } + break; + case "Fetch__Int32": + if (criteria is { Length: 1 } && criteria[0] is int __c2p0) + { + __operations.Fetch__Int32(__c2p0); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ArrayNullableGenericCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ArrayNullableGenericCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..947abc325c --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ArrayNullableGenericCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,110 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Fetch__Int32Array(int[] ids); + void Fetch__Nullable_1_Int32_String(int? id, string? name); + void Fetch__List_1_Int32(global::System.Collections.Generic.List ids); + void Fetch__ValueTuple_2_Int32_String((int Id, string Name) key); + void Fetch__Dictionary_2_String_List_1_Nullable_1_Int32Array(global::System.Collections.Generic.Dictionary>[] map); + } + + void IDataPortalOperations.Fetch__Int32Array(int[] ids) => Fetch(ids); + void IDataPortalOperations.Fetch__Nullable_1_Int32_String(int? id, string? name) => Fetch(id, name); + void IDataPortalOperations.Fetch__List_1_Int32(global::System.Collections.Generic.List ids) => Fetch(ids); + void IDataPortalOperations.Fetch__ValueTuple_2_Int32_String((int Id, string Name) key) => Fetch(key); + void IDataPortalOperations.Fetch__Dictionary_2_String_List_1_Nullable_1_Int32Array(global::System.Collections.Generic.Dictionary>[] map) => Fetch(map); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is int[] __c0p0 && __c0p0.GetType() == typeof(int[])) + { + __operations.Fetch__Int32Array(__c0p0); + return; + } + if (criteria is { Length: 2 } && criteria[0] is int __c1p0 && criteria[1] is string __c1p1) + { + __operations.Fetch__Nullable_1_Int32_String(__c1p0, __c1p1); + return; + } + if (criteria is { Length: 1 } && criteria[0] is global::System.Collections.Generic.List __c2p0 && __c2p0.GetType() == typeof(global::System.Collections.Generic.List)) + { + __operations.Fetch__List_1_Int32(__c2p0); + return; + } + if (criteria is { Length: 1 } && criteria[0] is global::System.ValueTuple __c3p0) + { + __operations.Fetch__ValueTuple_2_Int32_String(__c3p0); + return; + } + if (criteria is { Length: 1 } && criteria[0] is global::System.Collections.Generic.Dictionary>[] __c4p0 && __c4p0.GetType() == typeof(global::System.Collections.Generic.Dictionary>[])) + { + __operations.Fetch__Dictionary_2_String_List_1_Nullable_1_Int32Array(__c4p0); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Fetch__Int32Array": + if (criteria is { Length: 1 } && (criteria[0] is null || criteria[0] is int[])) + { + __operations.Fetch__Int32Array((int[])criteria[0]!); + return; + } + break; + case "Fetch__Nullable_1_Int32_String": + if (criteria is { Length: 2 } && (criteria[0] is null || criteria[0] is int) && (criteria[1] is null || criteria[1] is string)) + { + __operations.Fetch__Nullable_1_Int32_String((int?)criteria[0], (string?)criteria[1]!); + return; + } + break; + case "Fetch__List_1_Int32": + if (criteria is { Length: 1 } && (criteria[0] is null || criteria[0] is global::System.Collections.Generic.List)) + { + __operations.Fetch__List_1_Int32((global::System.Collections.Generic.List)criteria[0]!); + return; + } + break; + case "Fetch__ValueTuple_2_Int32_String": + if (criteria is { Length: 1 } && criteria[0] is global::System.ValueTuple __c3p0) + { + __operations.Fetch__ValueTuple_2_Int32_String(__c3p0); + return; + } + break; + case "Fetch__Dictionary_2_String_List_1_Nullable_1_Int32Array": + if (criteria is { Length: 1 } && (criteria[0] is null || criteria[0] is global::System.Collections.Generic.Dictionary>[])) + { + __operations.Fetch__Dictionary_2_String_List_1_Nullable_1_Int32Array((global::System.Collections.Generic.Dictionary>[])criteria[0]!); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AsyncMethod#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AsyncMethod#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..7289d37ab6 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AsyncMethod#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,56 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + global::System.Threading.Tasks.Task Fetch__Int32(int id); + } + + global::System.Threading.Tasks.Task IDataPortalOperations.Fetch__Int32(int id) => Fetch(id); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is int __c0p0) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); + await __operations.Fetch__Int32(__c0p0).ConfigureAwait(false); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Fetch__Int32": + if (criteria is { Length: 1 } && criteria[0] is int __c0p0) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); + await __operations.Fetch__Int32(__c0p0).ConfigureAwait(false); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..b7081044db --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs @@ -0,0 +1,134 @@ +//HintName: TestApp.LineItem.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class LineItem : global::TestApp.LineItem.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void CreateChild(); + void FetchChild__Int32(int id); + global::System.Threading.Tasks.Task InsertChild(global::TestApp.IDal dal); + global::System.Threading.Tasks.Task UpdateChild(global::TestApp.IDal dal); + global::System.Threading.Tasks.Task DeleteSelfChild(global::TestApp.IDal dal); + } + + void IDataPortalOperations.CreateChild() => CreateChild(); + void IDataPortalOperations.FetchChild__Int32(int id) => FetchChild(id); + global::System.Threading.Tasks.Task IDataPortalOperations.InsertChild(global::TestApp.IDal dal) => InsertChild(dal); + global::System.Threading.Tasks.Task IDataPortalOperations.UpdateChild(global::TestApp.IDal dal) => UpdateChild(dal); + global::System.Threading.Tasks.Task IDataPortalOperations.DeleteSelfChild(global::TestApp.IDal dal) => DeleteSelfChild(dal); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.CreateChildAttribute)) + { + if (criteria is null or { Length: 0 }) + { + __operations.CreateChild(); + return; + } + } + else if (operationType == typeof(global::Csla.FetchChildAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is int __c1p0) + { + __operations.FetchChild__Int32(__c1p0); + return; + } + } + else if (operationType == typeof(global::Csla.InsertChildAttribute)) + { + if (criteria is null or { Length: 0 }) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "InsertChild"); + var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + await __operations.InsertChild(__c2i0).ConfigureAwait(false); + return; + } + } + else if (operationType == typeof(global::Csla.UpdateChildAttribute)) + { + if (criteria is null or { Length: 0 }) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "UpdateChild"); + var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + await __operations.UpdateChild(__c3i0).ConfigureAwait(false); + return; + } + } + else if (operationType == typeof(global::Csla.DeleteSelfChildAttribute)) + { + if (criteria is null or { Length: 0 }) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "DeleteSelfChild"); + var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + await __operations.DeleteSelfChild(__c4i0).ConfigureAwait(false); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "CreateChild": + if (criteria is null or { Length: 0 }) + { + __operations.CreateChild(); + return; + } + break; + case "FetchChild__Int32": + if (criteria is { Length: 1 } && criteria[0] is int __c1p0) + { + __operations.FetchChild__Int32(__c1p0); + return; + } + break; + case "InsertChild": + if (criteria is null or { Length: 0 }) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "InsertChild"); + var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + await __operations.InsertChild(__c2i0).ConfigureAwait(false); + return; + } + break; + case "UpdateChild": + if (criteria is null or { Length: 0 }) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "UpdateChild"); + var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + await __operations.UpdateChild(__c3i0).ConfigureAwait(false); + return; + } + break; + case "DeleteSelfChild": + if (criteria is null or { Length: 0 }) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "DeleteSelfChild"); + var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + await __operations.DeleteSelfChild(__c4i0).ConfigureAwait(false); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..a2bf2d695e --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,134 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Create(); + void Fetch__Int32(int id); + global::System.Threading.Tasks.Task Insert(global::TestApp.IDal dal); + global::System.Threading.Tasks.Task Update(global::TestApp.IDal dal); + global::System.Threading.Tasks.Task DeleteSelf(global::TestApp.IDal dal); + } + + void IDataPortalOperations.Create() => Create(); + void IDataPortalOperations.Fetch__Int32(int id) => Fetch(id); + global::System.Threading.Tasks.Task IDataPortalOperations.Insert(global::TestApp.IDal dal) => Insert(dal); + global::System.Threading.Tasks.Task IDataPortalOperations.Update(global::TestApp.IDal dal) => Update(dal); + global::System.Threading.Tasks.Task IDataPortalOperations.DeleteSelf(global::TestApp.IDal dal) => DeleteSelf(dal); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.CreateAttribute)) + { + if (criteria is null or { Length: 0 }) + { + __operations.Create(); + return; + } + } + else if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is int __c1p0) + { + __operations.Fetch__Int32(__c1p0); + return; + } + } + else if (operationType == typeof(global::Csla.InsertAttribute)) + { + if (criteria is null or { Length: 0 }) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Insert"); + var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + await __operations.Insert(__c2i0).ConfigureAwait(false); + return; + } + } + else if (operationType == typeof(global::Csla.UpdateAttribute)) + { + if (criteria is null or { Length: 0 }) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Update"); + var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + await __operations.Update(__c3i0).ConfigureAwait(false); + return; + } + } + else if (operationType == typeof(global::Csla.DeleteSelfAttribute)) + { + if (criteria is null or { Length: 0 }) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "DeleteSelf"); + var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + await __operations.DeleteSelf(__c4i0).ConfigureAwait(false); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Create": + if (criteria is null or { Length: 0 }) + { + __operations.Create(); + return; + } + break; + case "Fetch__Int32": + if (criteria is { Length: 1 } && criteria[0] is int __c1p0) + { + __operations.Fetch__Int32(__c1p0); + return; + } + break; + case "Insert": + if (criteria is null or { Length: 0 }) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Insert"); + var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + await __operations.Insert(__c2i0).ConfigureAwait(false); + return; + } + break; + case "Update": + if (criteria is null or { Length: 0 }) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Update"); + var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + await __operations.Update(__c3i0).ConfigureAwait(false); + return; + } + break; + case "DeleteSelf": + if (criteria is null or { Length: 0 }) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "DeleteSelf"); + var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + await __operations.DeleteSelf(__c4i0).ConfigureAwait(false); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DeleteWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DeleteWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..51fed68d3b --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DeleteWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,54 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Delete__Int32(int id); + } + + void IDataPortalOperations.Delete__Int32(int id) => Delete(id); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.DeleteAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is int __c0p0) + { + __operations.Delete__Int32(__c0p0); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Delete__Int32": + if (criteria is { Length: 1 } && criteria[0] is int __c0p0) + { + __operations.Delete__Int32(__c0p0); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DerivedFromBaseWithOperations#TestApp.PersonBase_1.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DerivedFromBaseWithOperations#TestApp.PersonBase_1.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..445348c475 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DerivedFromBaseWithOperations#TestApp.PersonBase_1.DataPortalOperations.g.verified.cs @@ -0,0 +1,22 @@ +//HintName: TestApp.PersonBase_1.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonBase : global::TestApp.PersonBase.IDataPortalOperations + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Fetch__Int32(int id); + } + + void IDataPortalOperations.Fetch__Int32(int id) => Fetch(id); + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DerivedFromBaseWithOperations#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DerivedFromBaseWithOperations#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..d4981bac36 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DerivedFromBaseWithOperations#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,54 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal new interface IDataPortalOperations + { + void Fetch__String(string name); + } + + void IDataPortalOperations.Fetch__String(string name) => Fetch(name); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is string __c0p0) + { + __operations.Fetch__String(__c0p0); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Fetch__String": + if (criteria is { Length: 1 } && (criteria[0] is null || criteria[0] is string)) + { + __operations.Fetch__String((string)criteria[0]!); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..3afd11b461 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,60 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Fetch__Int32(int id); + void Fetch__Int32(int id, global::TestApp.IDal dal); + void Fetch__Int32_2(int id); + } + + void IDataPortalOperations.Fetch__Int32(int id) => Fetch(id); + void IDataPortalOperations.Fetch__Int32(int id, global::TestApp.IDal dal) => Fetch(id, dal); + void IDataPortalOperations.Fetch__Int32_2(int id) => Load(id); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is int __c0p0) + { + var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + __operations.Fetch__Int32(__c0p0, __c0i0); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Fetch__Int32": + if (criteria is { Length: 1 } && criteria[0] is int __c0p0) + { + var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + __operations.Fetch__Int32(__c0p0, __c0i0); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName.verified.txt new file mode 100644 index 0000000000..dde5061c54 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName.verified.txt @@ -0,0 +1,32 @@ +{ + Diagnostics: [ + { + Location: Source0.cs: (9,17)-(9,22), + Message: Methods 'Fetch(int, IDal)' and 'Fetch(int)' on 'PersonEdit' both map to data portal operation 'Fetch__Int32'; name-based dispatch uses 'Fetch(int, IDal)', + Severity: Info, + WarningLevel: 1, + Descriptor: { + Id: CSLADP002, + Title: Data portal operation methods share an operation name, + MessageFormat: Methods '{0}' and '{1}' on '{2}' both map to data portal operation '{3}'; name-based dispatch uses '{0}', + Category: Csla.DataPortal, + DefaultSeverity: Info, + IsEnabledByDefault: true + } + }, + { + Location: Source0.cs: (15,17)-(15,21), + Message: Methods 'Fetch(int, IDal)' and 'Load(int)' on 'PersonEdit' both map to data portal operation 'Fetch__Int32'; name-based dispatch uses 'Fetch(int, IDal)', + Severity: Info, + WarningLevel: 1, + Descriptor: { + Id: CSLADP002, + Title: Data portal operation methods share an operation name, + MessageFormat: Methods '{0}' and '{1}' on '{2}' both map to data portal operation '{3}'; name-based dispatch uses '{0}', + Category: Csla.DataPortal, + DefaultSeverity: Info, + IsEnabledByDefault: true + } + } + ] +} \ No newline at end of file diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ExecuteCommand#TestApp.MyCommand.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ExecuteCommand#TestApp.MyCommand.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..88ca1c8d6f --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ExecuteCommand#TestApp.MyCommand.DataPortalOperations.g.verified.cs @@ -0,0 +1,54 @@ +//HintName: TestApp.MyCommand.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class MyCommand : global::TestApp.MyCommand.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Execute(); + } + + void IDataPortalOperations.Execute() => Execute(); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.ExecuteAttribute)) + { + if (criteria is null or { Length: 0 }) + { + __operations.Execute(); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Execute": + if (criteria is null or { Length: 0 }) + { + __operations.Execute(); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ExistingNamedMapping#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ExistingNamedMapping#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..9948cdee01 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ExistingNamedMapping#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,37 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Fetch__Int32(int id); + } + + void IDataPortalOperations.Fetch__Int32(int id) => Fetch(id); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is int __c0p0) + { + __operations.Fetch__Int32(__c0p0); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.FileScopedNamespace#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.FileScopedNamespace#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..8656190c6c --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.FileScopedNamespace#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,71 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Create(); + void Fetch__Int32(int id); + } + + void IDataPortalOperations.Create() => Create(); + void IDataPortalOperations.Fetch__Int32(int id) => Fetch(id); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.CreateAttribute)) + { + if (criteria is null or { Length: 0 }) + { + __operations.Create(); + return; + } + } + else if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is int __c1p0) + { + __operations.Fetch__Int32(__c1p0); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Create": + if (criteria is null or { Length: 0 }) + { + __operations.Create(); + return; + } + break; + case "Fetch__Int32": + if (criteria is { Length: 1 } && criteria[0] is int __c1p0) + { + __operations.Fetch__Int32(__c1p0); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.GenericBusinessObject#TestApp.Lookup_1.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.GenericBusinessObject#TestApp.Lookup_1.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..ccbf49514d --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.GenericBusinessObject#TestApp.Lookup_1.DataPortalOperations.g.verified.cs @@ -0,0 +1,61 @@ +//HintName: TestApp.Lookup_1.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class Lookup : global::TestApp.Lookup.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Fetch__TKey(TKey key); + void Fetch__Int32(int id); + } + + void IDataPortalOperations.Fetch__TKey(TKey key) => Fetch(key); + void IDataPortalOperations.Fetch__Int32(int id) => Fetch(id); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is TKey __c0p0 && __c0p0.GetType() == typeof(TKey)) + { + __operations.Fetch__TKey(__c0p0); + return; + } + if (criteria is { Length: 1 } && criteria[0] is int __c1p0) + { + __operations.Fetch__Int32(__c1p0); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Fetch__Int32": + if (criteria is { Length: 1 } && criteria[0] is int __c0p0) + { + __operations.Fetch__Int32(__c0p0); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InjectBeforeCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InjectBeforeCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..455d714574 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InjectBeforeCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,58 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Fetch__Int32_String(global::TestApp.IDal dal, int id, global::TestApp.IDal? backup, string name); + } + + void IDataPortalOperations.Fetch__Int32_String(global::TestApp.IDal dal, int id, global::TestApp.IDal? backup, string name) => Fetch(dal, id, backup, name); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 2 } && criteria[0] is int __c0p0 && criteria[1] is string __c0p1) + { + var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), true)); + __operations.Fetch__Int32_String(__c0i0, __c0p0, __c0i1, __c0p1); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Fetch__Int32_String": + if (criteria is { Length: 2 } && criteria[0] is int __c0p0 && (criteria[1] is null || criteria[1] is string)) + { + var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), true)); + __operations.Fetch__Int32_String(__c0i0, __c0p0, __c0i1, (string)criteria[1]!); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.KeyedInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.KeyedInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..945e667f5d --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.KeyedInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,58 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Fetch__Int32(int id, global::TestApp.IDal dal, global::TestApp.IDal? secondary); + } + + void IDataPortalOperations.Fetch__Int32(int id, global::TestApp.IDal dal, global::TestApp.IDal? secondary) => Fetch(id, dal, secondary); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is int __c0p0) + { + var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), "primary", false))!; + var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), global::TestApp.DalKind.Secondary, true)); + __operations.Fetch__Int32(__c0p0, __c0i0, __c0i1); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Fetch__Int32": + if (criteria is { Length: 1 } && criteria[0] is int __c0p0) + { + var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), "primary", false))!; + var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), global::TestApp.DalKind.Secondary, true)); + __operations.Fetch__Int32(__c0p0, __c0i0, __c0i1); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.MultipleOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.MultipleOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..14cdc00634 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.MultipleOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,68 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Fetch__String(string name); + void Fetch__Int32(int id); + } + + void IDataPortalOperations.Fetch__String(string name) => Fetch(name); + void IDataPortalOperations.Fetch__Int32(int id) => Fetch(id); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is string __c0p0) + { + __operations.Fetch__String(__c0p0); + return; + } + if (criteria is { Length: 1 } && criteria[0] is int __c1p0) + { + __operations.Fetch__Int32(__c1p0); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Fetch__String": + if (criteria is { Length: 1 } && (criteria[0] is null || criteria[0] is string)) + { + __operations.Fetch__String((string)criteria[0]!); + return; + } + break; + case "Fetch__Int32": + if (criteria is { Length: 1 } && criteria[0] is int __c1p0) + { + __operations.Fetch__Int32(__c1p0); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.NestedClass#TestApp.Outer.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.NestedClass#TestApp.Outer.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..9fd380ce08 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.NestedClass#TestApp.Outer.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,57 @@ +//HintName: TestApp.Outer.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class Outer + { + partial class PersonEdit : global::TestApp.Outer.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Fetch__Int32(int id); + } + + void IDataPortalOperations.Fetch__Int32(int id) => Fetch(id); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is int __c0p0) + { + __operations.Fetch__Int32(__c0p0); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Fetch__Int32": + if (criteria is { Length: 1 } && criteria[0] is int __c0p0) + { + __operations.Fetch__Int32(__c0p0); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..e1231531d6 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,58 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + global::System.Threading.Tasks.Task Fetch__Int32(int id, global::TestApp.ILogger? logger); + } + + global::System.Threading.Tasks.Task IDataPortalOperations.Fetch__Int32(int id, global::TestApp.ILogger? logger) => Fetch(id, logger); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is int __c0p0) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); + var __c0i0 = (global::TestApp.ILogger?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.ILogger), true)); + await __operations.Fetch__Int32(__c0p0, __c0i0).ConfigureAwait(false); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Fetch__Int32": + if (criteria is { Length: 1 } && criteria[0] is int __c0p0) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); + var __c0i0 = (global::TestApp.ILogger?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.ILogger), true)); + await __operations.Fetch__Int32(__c0p0, __c0i0).ConfigureAwait(false); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..893bd2db41 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,58 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + global::System.Threading.Tasks.Task Insert(global::TestApp.IDal dal); + } + + global::System.Threading.Tasks.Task IDataPortalOperations.Insert(global::TestApp.IDal dal) => Insert(dal); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.InsertAttribute)) + { + if (criteria is null or { Length: 0 }) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Insert"); + var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + await __operations.Insert(__c0i0).ConfigureAwait(false); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Insert": + if (criteria is null or { Length: 0 }) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Insert"); + var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + await __operations.Insert(__c0i0).ConfigureAwait(false); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ParamsArrayAndKeywordNames#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ParamsArrayAndKeywordNames#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..7d0d5497d3 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ParamsArrayAndKeywordNames#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,56 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Fetch__ObjectArray(object[] values); + void Create__Int32_String(int @class, string @event); + } + + void IDataPortalOperations.Fetch__ObjectArray(object[] values) => Fetch(values); + void IDataPortalOperations.Create__Int32_String(int @class, string @event) => Create(@class, @event); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.CreateAttribute)) + { + if (criteria is { Length: 2 } && criteria[0] is int __c0p0 && criteria[1] is string __c0p1) + { + __operations.Create__Int32_String(__c0p0, __c0p1); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Create__Int32_String": + if (criteria is { Length: 2 } && criteria[0] is int __c0p0 && (criteria[1] is null || criteria[1] is string)) + { + __operations.Create__Int32_String(__c0p0, (string)criteria[1]!); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.PartialClassAcrossFiles#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.PartialClassAcrossFiles#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..8656190c6c --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.PartialClassAcrossFiles#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,71 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Create(); + void Fetch__Int32(int id); + } + + void IDataPortalOperations.Create() => Create(); + void IDataPortalOperations.Fetch__Int32(int id) => Fetch(id); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.CreateAttribute)) + { + if (criteria is null or { Length: 0 }) + { + __operations.Create(); + return; + } + } + else if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is int __c1p0) + { + __operations.Fetch__Int32(__c1p0); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Create": + if (criteria is null or { Length: 0 }) + { + __operations.Create(); + return; + } + break; + case "Fetch__Int32": + if (criteria is { Length: 1 } && criteria[0] is int __c1p0) + { + __operations.Fetch__Int32(__c1p0); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.RootAndChildAttributes#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.RootAndChildAttributes#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..aaefce3bd0 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.RootAndChildAttributes#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,71 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Create(); + void CreateChild(); + } + + void IDataPortalOperations.Create() => Create(); + void IDataPortalOperations.CreateChild() => Create(); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.CreateAttribute)) + { + if (criteria is null or { Length: 0 }) + { + __operations.Create(); + return; + } + } + else if (operationType == typeof(global::Csla.CreateChildAttribute)) + { + if (criteria is null or { Length: 0 }) + { + __operations.CreateChild(); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Create": + if (criteria is null or { Length: 0 }) + { + __operations.Create(); + return; + } + break; + case "CreateChild": + if (criteria is null or { Length: 0 }) + { + __operations.CreateChild(); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.RunLocalAndValueTask#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.RunLocalAndValueTask#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..0b2bd25d34 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.RunLocalAndValueTask#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,89 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Create(); + global::System.Threading.Tasks.ValueTask Fetch__Int32(int id); + global::System.Threading.Tasks.ValueTask Fetch__String(string name); + } + + void IDataPortalOperations.Create() => Create(); + global::System.Threading.Tasks.ValueTask IDataPortalOperations.Fetch__Int32(int id) => Fetch(id); + global::System.Threading.Tasks.ValueTask IDataPortalOperations.Fetch__String(string name) => Fetch(name); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.CreateAttribute)) + { + if (criteria is null or { Length: 0 }) + { + __operations.Create(); + return; + } + } + else if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is int __c1p0) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); + await __operations.Fetch__Int32(__c1p0).ConfigureAwait(false); + return; + } + if (criteria is { Length: 1 } && criteria[0] is string __c2p0) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); + await __operations.Fetch__String(__c2p0).ConfigureAwait(false); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Create": + if (criteria is null or { Length: 0 }) + { + __operations.Create(); + return; + } + break; + case "Fetch__Int32": + if (criteria is { Length: 1 } && criteria[0] is int __c1p0) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); + await __operations.Fetch__Int32(__c1p0).ConfigureAwait(false); + return; + } + break; + case "Fetch__String": + if (criteria is { Length: 1 } && (criteria[0] is null || criteria[0] is string)) + { + global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); + await __operations.Fetch__String((string)criteria[0]!).ConfigureAwait(false); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.SingleCreateNoParams#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.SingleCreateNoParams#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..78f5b02452 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.SingleCreateNoParams#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,54 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Create(); + } + + void IDataPortalOperations.Create() => Create(); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.CreateAttribute)) + { + if (criteria is null or { Length: 0 }) + { + __operations.Create(); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Create": + if (criteria is null or { Length: 0 }) + { + __operations.Create(); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.SingleFetchWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.SingleFetchWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..f3fc9a663b --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.SingleFetchWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,54 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Fetch__Int32(int id); + } + + void IDataPortalOperations.Fetch__Int32(int id) => Fetch(id); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is int __c0p0) + { + __operations.Fetch__Int32(__c0p0); + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Fetch__Int32": + if (criteria is { Length: 1 } && criteria[0] is int __c0p0) + { + __operations.Fetch__Int32(__c0p0); + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/StageCachingTester.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/StageCachingTester.cs new file mode 100644 index 0000000000..2d9761dcbe --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/StageCachingTester.cs @@ -0,0 +1,125 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// Verifies incremental generator pipeline stages are cacheable +//----------------------------------------------------------------------- +// Adapted from Csla.DataPortalExtensions by Stefan Ossendorf +// (https://github.com/StefanOssendorf/Csla.DataPortalExtensions), +// licensed under the MIT License. Copyright (c) 2023 Stefan Ossendorf. +//----------------------------------------------------------------------- +using System.Collections; +using System.Collections.Immutable; +using System.Reflection; +using FluentAssertions; +using Microsoft.CodeAnalysis; + +namespace Csla.Generator.AutoImplementProperties.CSharp.Tests.DataPortalOperations +{ + internal static class StageCachingTester + { + public static void VerifyStageCaching(string source, Type trackingNamesType) where T : IIncrementalGenerator, new() + { + var (driver, compilation) = DataPortalOperationsTestHelper.Setup(source, trackSteps: true); + var clonedCompilation = compilation.Clone(); + + driver = driver.RunGenerators(compilation); + var runResult1 = driver.GetRunResult(); + var runResult2 = driver.RunGenerators(clonedCompilation).GetRunResult(); + + AssertRunsEqual(runResult1, runResult2, GetTrackingNames(trackingNamesType)); + + runResult2.Results[0] + .TrackedOutputSteps + .SelectMany(step => step.Value) + .SelectMany(execution => execution.Outputs) + .Should() + .OnlyContain(output => output.Reason == IncrementalStepRunReason.Cached); + } + + private static string[] GetTrackingNames(Type trackingNamesType) + => trackingNamesType + .GetFields() + .Where(field => field.IsLiteral && !field.IsInitOnly && field.FieldType == typeof(string)) + .Select(field => (string?)field.GetRawConstantValue()) + .Where(name => !string.IsNullOrEmpty(name)) + .ToArray()!; + + private static void AssertRunsEqual(GeneratorDriverRunResult runResult1, GeneratorDriverRunResult runResult2, string[] trackingNames) + { + var trackedSteps1 = GetTrackedSteps(runResult1, trackingNames); + var trackedSteps2 = GetTrackedSteps(runResult2, trackingNames); + + trackedSteps1.Should() + .NotBeEmpty() + .And.HaveSameCount(trackedSteps2) + .And.ContainKeys(trackedSteps2.Keys); + + foreach (var (trackingName, runSteps1) in trackedSteps1) + AssertEqual(runSteps1, trackedSteps2[trackingName], trackingName); + + static Dictionary> GetTrackedSteps(GeneratorDriverRunResult runResult, string[] trackingNames) + => runResult + .Results[0] + .TrackedSteps + .Where(step => trackingNames.Contains(step.Key)) + .ToDictionary(step => step.Key, step => step.Value); + } + + private static void AssertEqual(ImmutableArray runSteps1, ImmutableArray runSteps2, string stepName) + { + runSteps1.Should().HaveSameCount(runSteps2); + + for (var i = 0; i < runSteps1.Length; i++) + { + var runStep1 = runSteps1[i]; + var runStep2 = runSteps2[i]; + + var outputs1 = runStep1.Outputs.Select(output => output.Value); + var outputs2 = runStep2.Outputs.Select(output => output.Value); + outputs1.Should().Equal(outputs2, $"because {stepName} should produce cacheable outputs"); + + runStep2.Outputs.Should().OnlyContain( + output => output.Reason == IncrementalStepRunReason.Cached || output.Reason == IncrementalStepRunReason.Unchanged, + $"{stepName} expected to have reason {IncrementalStepRunReason.Cached} or {IncrementalStepRunReason.Unchanged}"); + + AssertObjectGraph(runStep1, stepName); + } + } + + private static void AssertObjectGraph(IncrementalGeneratorRunStep runStep, string stepName) + { + var because = $"{stepName} shouldn't contain banned symbols"; + var visited = new HashSet(ReferenceEqualityComparer.Instance); + + foreach (var (value, _) in runStep.Outputs) + Visit(value); + + void Visit(object? node) + { + if (node is null || !visited.Add(node)) + return; + + node.Should().NotBeAssignableTo(because) + .And.NotBeAssignableTo(because) + .And.NotBeAssignableTo(because) + .And.NotBeAssignableTo(because); + + var type = node.GetType(); + if (type.IsPrimitive || type.IsEnum || type == typeof(string)) + return; + + if (node is IEnumerable collection) + { + foreach (var element in collection) + Visit(element); + return; + } + + foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) + Visit(field.GetValue(node)); + } + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=bool-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=bool-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 69916fe6bf..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=bool-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial bool? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=bool[]-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=bool[]-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index afe38fd635..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=bool[]-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial bool[]? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=bool[]_isNullable=True#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=bool[]_isNullable=True#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 764624d2f6..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=bool[]_isNullable=True#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial bool[] Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=bool_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=bool_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 7bef323b55..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=bool_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial bool Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=byte-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=byte-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index b17369f1cc..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=byte-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial byte? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=byte[]-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=byte[]-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 6405ac9e21..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=byte[]-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial byte[]? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=byte[]_isNullable=True#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=byte[]_isNullable=True#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 60cba132b6..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=byte[]_isNullable=True#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial byte[] Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=byte_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=byte_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index ce7fbfdeab..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=byte_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial byte Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=char-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=char-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 7687344adf..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=char-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial char? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=char[]-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=char[]-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index c21c684384..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=char[]-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial char[]? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=char[]_isNullable=True#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=char[]_isNullable=True#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 5feab6367a..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=char[]_isNullable=True#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial char[] Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=char_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=char_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index eaef8315dc..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=char_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial char Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=decimal-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=decimal-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 0343cc15fc..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=decimal-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial decimal? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=decimal[]-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=decimal[]-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 3bc0060cdc..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=decimal[]-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial decimal[]? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=decimal[]_isNullable=True#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=decimal[]_isNullable=True#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 36228611f1..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=decimal[]_isNullable=True#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial decimal[] Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=decimal_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=decimal_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 215d73734e..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=decimal_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial decimal Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=double-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=double-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index f1147813cb..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=double-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial double? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=double[]-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=double[]-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 9265abf5b0..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=double[]-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial double[]? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=double[]_isNullable=True#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=double[]_isNullable=True#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 14d08204ad..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=double[]_isNullable=True#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial double[] Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=double_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=double_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 5a2c513df2..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=double_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial double Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=float-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=float-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index f2239a78e6..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=float-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial float? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=float[]-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=float[]-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index e32eff6c1f..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=float[]-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial float[]? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=float[]_isNullable=True#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=float[]_isNullable=True#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index ccd1eb1b74..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=float[]_isNullable=True#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial float[] Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=float_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=float_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 0beae55dfa..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=float_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial float Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=int-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=int-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 74c1e446f3..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=int-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial int? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=int[]-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=int[]-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index b9f2266656..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=int[]-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial int[]? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=int[]_isNullable=True#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=int[]_isNullable=True#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index dfb91f097e..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=int[]_isNullable=True#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial int[] Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=int_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=int_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 102621c198..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=int_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial int Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=long-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=long-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index ad32a64fc1..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=long-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial long? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=long[]-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=long[]-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index e83abce87c..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=long[]-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial long[]? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=long[]_isNullable=True#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=long[]_isNullable=True#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 50d45d563e..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=long[]_isNullable=True#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial long[] Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=long_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=long_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index cf8caaf7e5..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=long_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial long Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=object-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=object-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index a42987b642..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=object-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial object? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=object[]-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=object[]-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index c6bf227f0f..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=object[]-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial object[]? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=object[]_isNullable=True#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=object[]_isNullable=True#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index ab99437761..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=object[]_isNullable=True#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial object[] Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=object_isNullable=True#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=object_isNullable=True#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 76956c1a26..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=object_isNullable=True#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial object Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=sbyte-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=sbyte-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 4cc203e6fd..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=sbyte-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial sbyte? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=sbyte[]-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=sbyte[]-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index dc7c035575..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=sbyte[]-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial sbyte[]? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=sbyte[]_isNullable=True#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=sbyte[]_isNullable=True#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index e5871c47c3..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=sbyte[]_isNullable=True#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial sbyte[] Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=sbyte_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=sbyte_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 5113172aba..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=sbyte_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial sbyte Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=short-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=short-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 8087a40ca5..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=short-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial short? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=short[]-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=short[]-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 106072e93d..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=short[]-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial short[]? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=short[]_isNullable=True#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=short[]_isNullable=True#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 3ddff9e00d..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=short[]_isNullable=True#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial short[] Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=short_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=short_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index e447538c8a..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=short_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial short Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=string-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=string-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 9a9f504268..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=string-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial string? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=string[]-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=string[]-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index a890d3f5b6..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=string[]-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial string[]? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=string[]_isNullable=True#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=string[]_isNullable=True#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index d6370bef50..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=string[]_isNullable=True#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial string[] Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=string_isNullable=True#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=string_isNullable=True#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index e0130eaecc..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=string_isNullable=True#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial string Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=uint-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=uint-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index abd3dc252e..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=uint-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial uint? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=uint[]-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=uint[]-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 8c224d70ed..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=uint[]-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial uint[]? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=uint[]_isNullable=True#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=uint[]_isNullable=True#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 125112f9fa..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=uint[]_isNullable=True#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial uint[] Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=uint_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=uint_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index bec9c9f83c..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=uint_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial uint Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ulong-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ulong-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 6ef0db6c01..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ulong-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial ulong? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ulong[]-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ulong[]-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index b95c2d9872..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ulong[]-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial ulong[]? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ulong[]_isNullable=True#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ulong[]_isNullable=True#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index e68617a4d6..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ulong[]_isNullable=True#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial ulong[] Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ulong_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ulong_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 4fcc485078..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ulong_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial ulong Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ushort-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ushort-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index b5e93db5ec..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ushort-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial ushort? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ushort[]-_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ushort[]-_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index d272c81fb6..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ushort[]-_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial ushort[]? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ushort[]_isNullable=True#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ushort[]_isNullable=True#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 79ed88b00a..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ushort[]_isNullable=True#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial ushort[] Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ushort_isNullable=False#BusinessBaseTestClass.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ushort_isNullable=False#BusinessBaseTestClass.g.received.cs deleted file mode 100644 index 6fb309228d..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.Case09_type=ushort_isNullable=False#BusinessBaseTestClass.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BusinessBaseTestClass.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BusinessBaseTestClass -{ - public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); - public partial ushort Name - { - get => GetProperty(NameProperty)!; - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.MyTestMethod#BOTest.g.received.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.MyTestMethod#BOTest.g.received.cs deleted file mode 100644 index bc22a023d8..0000000000 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/Snapshots/AutoImplementPropertiesGeneratorTests.MyTestMethod#BOTest.g.received.cs +++ /dev/null @@ -1,17 +0,0 @@ -//HintName: BOTest.g.cs -// -#nullable enable -using System; -using Csla; - -namespace Test; - -public partial class BOTest -{ - public static readonly PropertyInfo?>?> NameProperty = RegisterProperty?>?>(nameof(Name)); - public partial global::Csla.Core.MobileDictionary?>? Name - { - get => GetProperty(NameProperty); - private set => SetProperty(NameProperty, value); - } -} diff --git a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests.csproj b/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests.csproj deleted file mode 100644 index a439993e77..0000000000 --- a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests.csproj +++ /dev/null @@ -1,39 +0,0 @@ - - - - net10.0;net11.0 - preview - false - enable - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - diff --git a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/DataPortalInterfaceGeneratorTests.cs b/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/DataPortalInterfaceGeneratorTests.cs deleted file mode 100644 index fdcecdb727..0000000000 --- a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/DataPortalInterfaceGeneratorTests.cs +++ /dev/null @@ -1,312 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Marimer LLC. All rights reserved. -// Website: https://cslanet.com -// -//----------------------------------------------------------------------- -using Csla.Generator.DataPortalInterfaces.CSharp.Tests.Helpers; - -namespace Csla.Generator.DataPortalInterfaces.CSharp.Tests -{ - [TestClass] - public class DataPortalInterfaceGeneratorTests : VerifyBase - { - [TestMethod("Single Create operation with no parameters")] - public async Task SingleCreateNoParams() - { - var source = """ - using Csla; - - namespace TestApp - { - public partial class PersonEdit : Csla.BusinessBase - { - [Create] - private void Create() { } - } - } - """; - - await TestHelperVerify(source); - } - - [TestMethod("Single Fetch operation with one criteria parameter")] - public async Task SingleFetchWithCriteria() - { - var source = """ - using Csla; - - namespace TestApp - { - public partial class PersonEdit : Csla.BusinessBase - { - [Fetch] - private void Fetch(int id) { } - } - } - """; - - await TestHelperVerify(source); - } - - [TestMethod("Operation method with Inject parameter")] - public async Task OperationWithInjectParam() - { - var source = """ - using Csla; - using System.Threading.Tasks; - - namespace TestApp - { - public interface IDal { } - - public partial class PersonEdit : Csla.BusinessBase - { - [Insert] - private async Task Insert([Inject] IDal dal) { } - } - } - """; - - await TestHelperVerify(source); - } - - [TestMethod("Operation method with Inject AllowNull parameter")] - public async Task OperationWithInjectAllowNull() - { - var source = """ - using Csla; - using System.Threading.Tasks; - - namespace TestApp - { - public interface ILogger { } - - public partial class PersonEdit : Csla.BusinessBase - { - [Fetch] - private async Task Fetch(int id, [Inject(AllowNull = true)] ILogger? logger) { } - } - } - """; - - await TestHelperVerify(source); - } - - [TestMethod("Multiple overloads of same Fetch operation")] - public async Task MultipleOverloads() - { - var source = """ - using Csla; - - namespace TestApp - { - public partial class PersonEdit : Csla.BusinessBase - { - [Fetch] - private void Fetch(string name) { } - - [Fetch] - private void Fetch(int id) { } - } - } - """; - - await TestHelperVerify(source); - } - - [TestMethod("Method with both root and child attributes")] - public async Task RootAndChildAttributes() - { - var source = """ - using Csla; - - namespace TestApp - { - public partial class PersonEdit : Csla.BusinessBase - { - [Create] - [CreateChild] - private void Create() { } - } - } - """; - - await TestHelperVerify(source); - } - - [TestMethod("Async method returning Task")] - public async Task AsyncMethod() - { - var source = """ - using Csla; - using System.Threading.Tasks; - - namespace TestApp - { - public partial class PersonEdit : Csla.BusinessBase - { - [Fetch] - private async Task Fetch(int id) { await Task.CompletedTask; } - } - } - """; - - await TestHelperVerify(source); - } - - [TestMethod("File-scoped namespace")] - public async Task FileScopedNamespace() - { - var source = """ - using Csla; - - namespace TestApp; - - public partial class PersonEdit : Csla.BusinessBase - { - [Create] - private void Create() { } - - [Fetch] - private void Fetch(int id) { } - } - """; - - await TestHelperVerify(source); - } - - [TestMethod("Complete CRUD operations")] - public async Task CompleteCrud() - { - var source = """ - using Csla; - using System.Threading.Tasks; - - namespace TestApp - { - public interface IDal { } - - public partial class PersonEdit : Csla.BusinessBase - { - [Create] - private void Create() { } - - [Fetch] - private void Fetch(int id) { } - - [Insert] - private async Task Insert([Inject] IDal dal) { await Task.CompletedTask; } - - [Update] - private async Task Update([Inject] IDal dal) { await Task.CompletedTask; } - - [DeleteSelf] - private async Task DeleteSelf([Inject] IDal dal) { await Task.CompletedTask; } - } - } - """; - - await TestHelperVerify(source); - } - - [TestMethod("Delete operation with criteria")] - public async Task DeleteWithCriteria() - { - var source = """ - using Csla; - - namespace TestApp - { - public partial class PersonEdit : Csla.BusinessBase - { - [Delete] - private void Delete(int id) { } - } - } - """; - - await TestHelperVerify(source); - } - - [TestMethod("Child operations")] - public async Task ChildOperations() - { - var source = """ - using Csla; - using System.Threading.Tasks; - - namespace TestApp - { - public interface IDal { } - - public partial class LineItem : Csla.BusinessBase - { - [CreateChild] - private void CreateChild() { } - - [FetchChild] - private void FetchChild(int id) { } - - [InsertChild] - private async Task InsertChild([Inject] IDal dal) { await Task.CompletedTask; } - - [UpdateChild] - private async Task UpdateChild([Inject] IDal dal) { await Task.CompletedTask; } - - [DeleteSelfChild] - private async Task DeleteSelfChild([Inject] IDal dal) { await Task.CompletedTask; } - } - } - """; - - await TestHelperVerify(source); - } - - [TestMethod("Execute operation on command object")] - public async Task ExecuteCommand() - { - var source = """ - using Csla; - - namespace TestApp - { - public partial class MyCommand : Csla.CommandBase - { - [Execute] - private void Execute() { } - } - } - """; - - await TestHelperVerify(source); - } - - [TestMethod("Nested class with data portal operations")] - public async Task NestedClass() - { - var source = """ - using Csla; - - namespace TestApp - { - public partial class Outer - { - public partial class PersonEdit : Csla.BusinessBase - { - [Fetch] - private void Fetch(int id) { } - } - } - } - """; - - await TestHelperVerify(source); - } - - private static async Task TestHelperVerify(string source, params string[]? additionalSources) - { - await TestHelper.Verify(source, additionalSources); - } - } -} diff --git a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.AsyncMethod#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.AsyncMethod#TestApp.PersonEdit.DataPortalOperations.g.verified.cs deleted file mode 100644 index 825b39f51c..0000000000 --- a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.AsyncMethod#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ /dev/null @@ -1,39 +0,0 @@ -//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs -// -#nullable enable - -namespace TestApp -{ - public partial class PersonEdit : Csla.Server.IDataPortalOperationMapping, Csla.Server.IDataPortalOperationNamedMapping - { - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( - global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - if (operationType == typeof(Csla.FetchAttribute)) - { - if (criteria is { Length: 1 } && criteria[0] is int p0_int) - { - await Fetch(p0_int).ConfigureAwait(false); - return; - } - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); - } - - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( - string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - switch (operationName) - { - case "Fetch__Int32": - if (criteria is { Length: 1 } && criteria[0] is int p0_int) - { - await Fetch(p0_int).ConfigureAwait(false); - return; - } - break; - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); - } - } -} diff --git a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs deleted file mode 100644 index 6dcbb4cab2..0000000000 --- a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs +++ /dev/null @@ -1,105 +0,0 @@ -//HintName: TestApp.LineItem.DataPortalOperations.g.cs -// -#nullable enable - -namespace TestApp -{ - public partial class LineItem : Csla.Server.IDataPortalOperationMapping, Csla.Server.IDataPortalOperationNamedMapping - { - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( - global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - if (operationType == typeof(Csla.CreateChildAttribute)) - { - if (criteria is null or { Length: 0 }) - { - CreateChild(); - return; - } - } - else if (operationType == typeof(Csla.FetchChildAttribute)) - { - if (criteria is { Length: 1 } && criteria[0] is int p0_int) - { - FetchChild(p0_int); - return; - } - } - else if (operationType == typeof(Csla.InsertChildAttribute)) - { - if (criteria is null or { Length: 0 }) - { - var dal = (global::TestApp.IDal)(serviceProvider.GetService(typeof(global::TestApp.IDal)) ?? throw new global::System.InvalidOperationException($"No service for type '{typeof(global::TestApp.IDal)}' has been registered.")); - await InsertChild(dal).ConfigureAwait(false); - return; - } - } - else if (operationType == typeof(Csla.UpdateChildAttribute)) - { - if (criteria is null or { Length: 0 }) - { - var dal = (global::TestApp.IDal)(serviceProvider.GetService(typeof(global::TestApp.IDal)) ?? throw new global::System.InvalidOperationException($"No service for type '{typeof(global::TestApp.IDal)}' has been registered.")); - await UpdateChild(dal).ConfigureAwait(false); - return; - } - } - else if (operationType == typeof(Csla.DeleteSelfChildAttribute)) - { - if (criteria is null or { Length: 0 }) - { - var dal = (global::TestApp.IDal)(serviceProvider.GetService(typeof(global::TestApp.IDal)) ?? throw new global::System.InvalidOperationException($"No service for type '{typeof(global::TestApp.IDal)}' has been registered.")); - await DeleteSelfChild(dal).ConfigureAwait(false); - return; - } - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); - } - - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( - string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - switch (operationName) - { - case "CreateChild": - if (criteria is null or { Length: 0 }) - { - CreateChild(); - return; - } - break; - case "FetchChild__Int32": - if (criteria is { Length: 1 } && criteria[0] is int p0_int) - { - FetchChild(p0_int); - return; - } - break; - case "InsertChild": - if (criteria is null or { Length: 0 }) - { - var dal = (global::TestApp.IDal)(serviceProvider.GetService(typeof(global::TestApp.IDal)) ?? throw new global::System.InvalidOperationException($"No service for type '{typeof(global::TestApp.IDal)}' has been registered.")); - await InsertChild(dal).ConfigureAwait(false); - return; - } - break; - case "UpdateChild": - if (criteria is null or { Length: 0 }) - { - var dal = (global::TestApp.IDal)(serviceProvider.GetService(typeof(global::TestApp.IDal)) ?? throw new global::System.InvalidOperationException($"No service for type '{typeof(global::TestApp.IDal)}' has been registered.")); - await UpdateChild(dal).ConfigureAwait(false); - return; - } - break; - case "DeleteSelfChild": - if (criteria is null or { Length: 0 }) - { - var dal = (global::TestApp.IDal)(serviceProvider.GetService(typeof(global::TestApp.IDal)) ?? throw new global::System.InvalidOperationException($"No service for type '{typeof(global::TestApp.IDal)}' has been registered.")); - await DeleteSelfChild(dal).ConfigureAwait(false); - return; - } - break; - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); - } - } -} diff --git a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs deleted file mode 100644 index ab81066256..0000000000 --- a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ /dev/null @@ -1,105 +0,0 @@ -//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs -// -#nullable enable - -namespace TestApp -{ - public partial class PersonEdit : Csla.Server.IDataPortalOperationMapping, Csla.Server.IDataPortalOperationNamedMapping - { - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( - global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - if (operationType == typeof(Csla.CreateAttribute)) - { - if (criteria is null or { Length: 0 }) - { - Create(); - return; - } - } - else if (operationType == typeof(Csla.FetchAttribute)) - { - if (criteria is { Length: 1 } && criteria[0] is int p0_int) - { - Fetch(p0_int); - return; - } - } - else if (operationType == typeof(Csla.InsertAttribute)) - { - if (criteria is null or { Length: 0 }) - { - var dal = (global::TestApp.IDal)(serviceProvider.GetService(typeof(global::TestApp.IDal)) ?? throw new global::System.InvalidOperationException($"No service for type '{typeof(global::TestApp.IDal)}' has been registered.")); - await Insert(dal).ConfigureAwait(false); - return; - } - } - else if (operationType == typeof(Csla.UpdateAttribute)) - { - if (criteria is null or { Length: 0 }) - { - var dal = (global::TestApp.IDal)(serviceProvider.GetService(typeof(global::TestApp.IDal)) ?? throw new global::System.InvalidOperationException($"No service for type '{typeof(global::TestApp.IDal)}' has been registered.")); - await Update(dal).ConfigureAwait(false); - return; - } - } - else if (operationType == typeof(Csla.DeleteSelfAttribute)) - { - if (criteria is null or { Length: 0 }) - { - var dal = (global::TestApp.IDal)(serviceProvider.GetService(typeof(global::TestApp.IDal)) ?? throw new global::System.InvalidOperationException($"No service for type '{typeof(global::TestApp.IDal)}' has been registered.")); - await DeleteSelf(dal).ConfigureAwait(false); - return; - } - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); - } - - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( - string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - switch (operationName) - { - case "Create": - if (criteria is null or { Length: 0 }) - { - Create(); - return; - } - break; - case "Fetch__Int32": - if (criteria is { Length: 1 } && criteria[0] is int p0_int) - { - Fetch(p0_int); - return; - } - break; - case "Insert": - if (criteria is null or { Length: 0 }) - { - var dal = (global::TestApp.IDal)(serviceProvider.GetService(typeof(global::TestApp.IDal)) ?? throw new global::System.InvalidOperationException($"No service for type '{typeof(global::TestApp.IDal)}' has been registered.")); - await Insert(dal).ConfigureAwait(false); - return; - } - break; - case "Update": - if (criteria is null or { Length: 0 }) - { - var dal = (global::TestApp.IDal)(serviceProvider.GetService(typeof(global::TestApp.IDal)) ?? throw new global::System.InvalidOperationException($"No service for type '{typeof(global::TestApp.IDal)}' has been registered.")); - await Update(dal).ConfigureAwait(false); - return; - } - break; - case "DeleteSelf": - if (criteria is null or { Length: 0 }) - { - var dal = (global::TestApp.IDal)(serviceProvider.GetService(typeof(global::TestApp.IDal)) ?? throw new global::System.InvalidOperationException($"No service for type '{typeof(global::TestApp.IDal)}' has been registered.")); - await DeleteSelf(dal).ConfigureAwait(false); - return; - } - break; - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); - } - } -} diff --git a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.DeleteWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.DeleteWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs deleted file mode 100644 index c604313f49..0000000000 --- a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.DeleteWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ /dev/null @@ -1,39 +0,0 @@ -//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs -// -#nullable enable - -namespace TestApp -{ - public partial class PersonEdit : Csla.Server.IDataPortalOperationMapping, Csla.Server.IDataPortalOperationNamedMapping - { - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( - global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - if (operationType == typeof(Csla.DeleteAttribute)) - { - if (criteria is { Length: 1 } && criteria[0] is int p0_int) - { - Delete(p0_int); - return; - } - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); - } - - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( - string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - switch (operationName) - { - case "Delete__Int32": - if (criteria is { Length: 1 } && criteria[0] is int p0_int) - { - Delete(p0_int); - return; - } - break; - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); - } - } -} diff --git a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.ExecuteCommand#TestApp.MyCommand.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.ExecuteCommand#TestApp.MyCommand.DataPortalOperations.g.verified.cs deleted file mode 100644 index 59e4789567..0000000000 --- a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.ExecuteCommand#TestApp.MyCommand.DataPortalOperations.g.verified.cs +++ /dev/null @@ -1,39 +0,0 @@ -//HintName: TestApp.MyCommand.DataPortalOperations.g.cs -// -#nullable enable - -namespace TestApp -{ - public partial class MyCommand : Csla.Server.IDataPortalOperationMapping, Csla.Server.IDataPortalOperationNamedMapping - { - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( - global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - if (operationType == typeof(Csla.ExecuteAttribute)) - { - if (criteria is null or { Length: 0 }) - { - Execute(); - return; - } - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); - } - - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( - string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - switch (operationName) - { - case "Execute": - if (criteria is null or { Length: 0 }) - { - Execute(); - return; - } - break; - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); - } - } -} diff --git a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.FileScopedNamespace#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.FileScopedNamespace#TestApp.PersonEdit.DataPortalOperations.g.verified.cs deleted file mode 100644 index a024ca2cff..0000000000 --- a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.FileScopedNamespace#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ /dev/null @@ -1,54 +0,0 @@ -//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs -// -#nullable enable - -namespace TestApp -{ - public partial class PersonEdit : Csla.Server.IDataPortalOperationMapping, Csla.Server.IDataPortalOperationNamedMapping - { - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( - global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - if (operationType == typeof(Csla.CreateAttribute)) - { - if (criteria is null or { Length: 0 }) - { - Create(); - return; - } - } - else if (operationType == typeof(Csla.FetchAttribute)) - { - if (criteria is { Length: 1 } && criteria[0] is int p0_int) - { - Fetch(p0_int); - return; - } - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); - } - - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( - string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - switch (operationName) - { - case "Create": - if (criteria is null or { Length: 0 }) - { - Create(); - return; - } - break; - case "Fetch__Int32": - if (criteria is { Length: 1 } && criteria[0] is int p0_int) - { - Fetch(p0_int); - return; - } - break; - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); - } - } -} diff --git a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.MultipleOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.MultipleOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs deleted file mode 100644 index dd8fac0098..0000000000 --- a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.MultipleOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ /dev/null @@ -1,51 +0,0 @@ -//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs -// -#nullable enable - -namespace TestApp -{ - public partial class PersonEdit : Csla.Server.IDataPortalOperationMapping, Csla.Server.IDataPortalOperationNamedMapping - { - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( - global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - if (operationType == typeof(Csla.FetchAttribute)) - { - if (criteria is { Length: 1 } && criteria[0] is string p0_string) - { - Fetch(p0_string); - return; - } - if (criteria is { Length: 1 } && criteria[0] is int p0_int) - { - Fetch(p0_int); - return; - } - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); - } - - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( - string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - switch (operationName) - { - case "Fetch__String": - if (criteria is { Length: 1 } && criteria[0] is string p0_string) - { - Fetch(p0_string); - return; - } - break; - case "Fetch__Int32": - if (criteria is { Length: 1 } && criteria[0] is int p0_int) - { - Fetch(p0_int); - return; - } - break; - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); - } - } -} diff --git a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.NestedClass#TestApp.Outer.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.NestedClass#TestApp.Outer.PersonEdit.DataPortalOperations.g.verified.cs deleted file mode 100644 index bb807b6b31..0000000000 --- a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.NestedClass#TestApp.Outer.PersonEdit.DataPortalOperations.g.verified.cs +++ /dev/null @@ -1,42 +0,0 @@ -//HintName: TestApp.Outer.PersonEdit.DataPortalOperations.g.cs -// -#nullable enable - -namespace TestApp -{ - public partial class Outer - { - public partial class PersonEdit : Csla.Server.IDataPortalOperationMapping, Csla.Server.IDataPortalOperationNamedMapping - { - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( - global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - if (operationType == typeof(Csla.FetchAttribute)) - { - if (criteria is { Length: 1 } && criteria[0] is int p0_int) - { - Fetch(p0_int); - return; - } - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); - } - - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( - string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - switch (operationName) - { - case "Fetch__Int32": - if (criteria is { Length: 1 } && criteria[0] is int p0_int) - { - Fetch(p0_int); - return; - } - break; - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); - } - } - } -} diff --git a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs deleted file mode 100644 index b8bf49e1d1..0000000000 --- a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ /dev/null @@ -1,41 +0,0 @@ -//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs -// -#nullable enable - -namespace TestApp -{ - public partial class PersonEdit : Csla.Server.IDataPortalOperationMapping, Csla.Server.IDataPortalOperationNamedMapping - { - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( - global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - if (operationType == typeof(Csla.FetchAttribute)) - { - if (criteria is { Length: 1 } && criteria[0] is int p0_int) - { - var logger = (global::TestApp.ILogger?)serviceProvider.GetService(typeof(global::TestApp.ILogger)); - await Fetch(p0_int, logger).ConfigureAwait(false); - return; - } - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); - } - - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( - string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - switch (operationName) - { - case "Fetch__Int32": - if (criteria is { Length: 1 } && criteria[0] is int p0_int) - { - var logger = (global::TestApp.ILogger?)serviceProvider.GetService(typeof(global::TestApp.ILogger)); - await Fetch(p0_int, logger).ConfigureAwait(false); - return; - } - break; - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); - } - } -} diff --git a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs deleted file mode 100644 index af73cdb9a5..0000000000 --- a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ /dev/null @@ -1,41 +0,0 @@ -//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs -// -#nullable enable - -namespace TestApp -{ - public partial class PersonEdit : Csla.Server.IDataPortalOperationMapping, Csla.Server.IDataPortalOperationNamedMapping - { - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( - global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - if (operationType == typeof(Csla.InsertAttribute)) - { - if (criteria is null or { Length: 0 }) - { - var dal = (global::TestApp.IDal)(serviceProvider.GetService(typeof(global::TestApp.IDal)) ?? throw new global::System.InvalidOperationException($"No service for type '{typeof(global::TestApp.IDal)}' has been registered.")); - await Insert(dal).ConfigureAwait(false); - return; - } - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); - } - - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( - string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - switch (operationName) - { - case "Insert": - if (criteria is null or { Length: 0 }) - { - var dal = (global::TestApp.IDal)(serviceProvider.GetService(typeof(global::TestApp.IDal)) ?? throw new global::System.InvalidOperationException($"No service for type '{typeof(global::TestApp.IDal)}' has been registered.")); - await Insert(dal).ConfigureAwait(false); - return; - } - break; - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); - } - } -} diff --git a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.RootAndChildAttributes#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.RootAndChildAttributes#TestApp.PersonEdit.DataPortalOperations.g.verified.cs deleted file mode 100644 index 4826fada4d..0000000000 --- a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.RootAndChildAttributes#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ /dev/null @@ -1,54 +0,0 @@ -//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs -// -#nullable enable - -namespace TestApp -{ - public partial class PersonEdit : Csla.Server.IDataPortalOperationMapping, Csla.Server.IDataPortalOperationNamedMapping - { - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( - global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - if (operationType == typeof(Csla.CreateAttribute)) - { - if (criteria is null or { Length: 0 }) - { - Create(); - return; - } - } - else if (operationType == typeof(Csla.CreateChildAttribute)) - { - if (criteria is null or { Length: 0 }) - { - Create(); - return; - } - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); - } - - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( - string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - switch (operationName) - { - case "Create": - if (criteria is null or { Length: 0 }) - { - Create(); - return; - } - break; - case "CreateChild": - if (criteria is null or { Length: 0 }) - { - Create(); - return; - } - break; - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); - } - } -} diff --git a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.SingleCreateNoParams#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.SingleCreateNoParams#TestApp.PersonEdit.DataPortalOperations.g.verified.cs deleted file mode 100644 index 19785483bf..0000000000 --- a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.SingleCreateNoParams#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ /dev/null @@ -1,39 +0,0 @@ -//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs -// -#nullable enable - -namespace TestApp -{ - public partial class PersonEdit : Csla.Server.IDataPortalOperationMapping, Csla.Server.IDataPortalOperationNamedMapping - { - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( - global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - if (operationType == typeof(Csla.CreateAttribute)) - { - if (criteria is null or { Length: 0 }) - { - Create(); - return; - } - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); - } - - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( - string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - switch (operationName) - { - case "Create": - if (criteria is null or { Length: 0 }) - { - Create(); - return; - } - break; - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); - } - } -} diff --git a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.SingleFetchWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.SingleFetchWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs deleted file mode 100644 index 3f44ca0017..0000000000 --- a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/Snapshots/DataPortalInterfaceGeneratorTests.SingleFetchWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ /dev/null @@ -1,39 +0,0 @@ -//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs -// -#nullable enable - -namespace TestApp -{ - public partial class PersonEdit : Csla.Server.IDataPortalOperationMapping, Csla.Server.IDataPortalOperationNamedMapping - { - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( - global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - if (operationType == typeof(Csla.FetchAttribute)) - { - if (criteria is { Length: 1 } && criteria[0] is int p0_int) - { - Fetch(p0_int); - return; - } - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); - } - - async global::System.Threading.Tasks.Task Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( - string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) - { - switch (operationName) - { - case "Fetch__Int32": - if (criteria is { Length: 1 } && criteria[0] is int p0_int) - { - Fetch(p0_int); - return; - } - break; - } - throw new Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); - } - } -} diff --git a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/TestHelper.cs b/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/TestHelper.cs deleted file mode 100644 index c076969e10..0000000000 --- a/Source/tests/Csla.Generator.DataPortalInterfaces.CSharp.Tests/Helpers/TestHelper.cs +++ /dev/null @@ -1,91 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Marimer LLC. All rights reserved. -// Website: https://cslanet.com -// -//----------------------------------------------------------------------- -using FluentAssertions; -using FluentAssertions.Execution; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; - -namespace Csla.Generator.DataPortalInterfaces.CSharp.Tests.Helpers -{ - public static class TestHelper where T : IIncrementalGenerator, new() - { - public static Task Verify(string source, IEnumerable? additionalSources = null) - { - var syntaxTrees = new List - { - CSharpSyntaxTree.ParseText(source) - }; - - foreach (var src in additionalSources ?? []) - { - syntaxTrees.Add(CSharpSyntaxTree.ParseText(src)); - } - - var references = AppDomain.CurrentDomain.GetAssemblies() - .Where(a => !a.IsDynamic && !string.IsNullOrWhiteSpace(a.Location)) - .Select(a => MetadataReference.CreateFromFile(a.Location)) - .Concat([ - MetadataReference.CreateFromFile(typeof(FetchAttribute).Assembly.Location) - ]); - - var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) - .WithNullableContextOptions(NullableContextOptions.Enable) - .WithSpecificDiagnosticOptions(new Dictionary { { "CS8019", ReportDiagnostic.Suppress } }); - - CSharpCompilation compilation = CSharpCompilation.Create( - assemblyName: "Tests", - syntaxTrees: syntaxTrees, - references: references, - options: compilationOptions); - - var generator = new T().AsSourceGenerator(); - GeneratorDriver driver = CSharpGeneratorDriver.Create(generator); - driver = driver.RunGeneratorsAndUpdateCompilation(compilation, out var outputCompilation, out var diagnostics); - - using (new AssertionScope()) - { - outputCompilation.GetDiagnostics() - .Where(d => d.Severity == DiagnosticSeverity.Error) - .Should().BeEmpty(); - diagnostics.Should().BeEmpty(); - } - - return Verifier.Verify(driver).UseDirectory("Snapshots"); - } - - public static Task VerifyWithDiagnostics(string source) - { - var syntaxTrees = new List - { - CSharpSyntaxTree.ParseText(source) - }; - - var references = AppDomain.CurrentDomain.GetAssemblies() - .Where(a => !a.IsDynamic && !string.IsNullOrWhiteSpace(a.Location)) - .Select(a => MetadataReference.CreateFromFile(a.Location)) - .Concat([ - MetadataReference.CreateFromFile(typeof(FetchAttribute).Assembly.Location) - ]); - - var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) - .WithNullableContextOptions(NullableContextOptions.Enable) - .WithSpecificDiagnosticOptions(new Dictionary { { "CS8019", ReportDiagnostic.Suppress } }); - - CSharpCompilation compilation = CSharpCompilation.Create( - assemblyName: "Tests", - syntaxTrees: syntaxTrees, - references: references, - options: compilationOptions); - - var generator = new T().AsSourceGenerator(); - GeneratorDriver driver = CSharpGeneratorDriver.Create(generator); - driver = driver.RunGeneratorsAndUpdateCompilation(compilation, out _, out _); - - return Verifier.Verify(driver).UseDirectory("Snapshots"); - } - } -} diff --git a/Source/tests/Csla.test/Csla.Tests.csproj b/Source/tests/Csla.test/Csla.Tests.csproj index 2c9236cc84..aa36986bf5 100644 --- a/Source/tests/Csla.test/Csla.Tests.csproj +++ b/Source/tests/Csla.test/Csla.Tests.csproj @@ -68,7 +68,6 @@ - From 4200ec247d4b8a0523a873dd7c37e4fa04d2897f Mon Sep 17 00:00:00 2001 From: Rockford lhotka Date: Sun, 13 Sep 2026 17:52:33 -0500 Subject: [PATCH 03/10] #4359 Add CSLA0024 partial-type analyzer with code fix and CSLA0025 extension usage analyzer CSLA0024 warns when a class declaring data portal operation methods, or any of its containing types, is not partial; the code fix adds partial to the class and all non-partial containing types. CSLA0025 warns when untyped criteria-based IDataPortal, IChildDataPortal, or DataPortal methods are called for a business type marked with [DataPortalExtensions]. Generated code is skipped. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Dn4aKN7kqcG78x1HBWApk3 --- .../AnalyzerReleases.Unshipped.md | 4 +- .../Csla.Analyzers/Constants.cs | 8 + .../Properties/Resources.Designer.cs | 45 +++ .../Csla.Analyzers/Properties/Resources.resx | 15 + ...rationsShouldBePartialAddPartialCodeFix.cs | 86 ++++++ ...peWithOperationsShouldBePartialAnalyzer.cs | 143 ++++++++++ ...rationsShouldBePartialAnalyzerConstants.cs | 31 ++ ...UseGeneratedDataPortalExtensionAnalyzer.cs | 168 +++++++++++ ...tedDataPortalExtensionAnalyzerConstants.cs | 20 ++ ...nsShouldBePartialAddPartialCodeFixTests.cs | 183 ++++++++++++ ...hOperationsShouldBePartialAnalyzerTests.cs | 264 ++++++++++++++++++ ...neratedDataPortalExtensionAnalyzerTests.cs | 246 ++++++++++++++++ ...peWithOperationsShouldBePartialAnalyzer.md | 52 ++++ ...UseGeneratedDataPortalExtensionAnalyzer.md | 39 +++ docs/analyzers/index.md | 27 +- 15 files changed, 1329 insertions(+), 2 deletions(-) create mode 100644 Source/Csla.Analyzers/Csla.Analyzers/TypeWithOperationsShouldBePartialAddPartialCodeFix.cs create mode 100644 Source/Csla.Analyzers/Csla.Analyzers/TypeWithOperationsShouldBePartialAnalyzer.cs create mode 100644 Source/Csla.Analyzers/Csla.Analyzers/TypeWithOperationsShouldBePartialAnalyzerConstants.cs create mode 100644 Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzer.cs create mode 100644 Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzerConstants.cs create mode 100644 Source/tests/Csla.Analyzers.Tests/TypeWithOperationsShouldBePartialAddPartialCodeFixTests.cs create mode 100644 Source/tests/Csla.Analyzers.Tests/TypeWithOperationsShouldBePartialAnalyzerTests.cs create mode 100644 Source/tests/Csla.Analyzers.Tests/UseGeneratedDataPortalExtensionAnalyzerTests.cs create mode 100644 docs/analyzers/CSLA0024-TypeWithOperationsShouldBePartialAnalyzer.md create mode 100644 docs/analyzers/CSLA0025-UseGeneratedDataPortalExtensionAnalyzer.md diff --git a/Source/Csla.Analyzers/Csla.Analyzers/AnalyzerReleases.Unshipped.md b/Source/Csla.Analyzers/Csla.Analyzers/AnalyzerReleases.Unshipped.md index c78e476580..644dc4662c 100644 --- a/Source/Csla.Analyzers/Csla.Analyzers/AnalyzerReleases.Unshipped.md +++ b/Source/Csla.Analyzers/Csla.Analyzers/AnalyzerReleases.Unshipped.md @@ -25,4 +25,6 @@ CSLA0019 | Usage | Error | FindRefAndOutParametersInOperationsAnalyzer CSLA0020 | Usage | Warning | ObjectAuthorizationRulesAttributeMissing CSLA0021 | Usage | Info | ObjectAuthorizationRulesPublic CSLA0022 | Usage | Warning | ObjectAuthorizationRulesStatic -CSLA0023 | Refactoring | Info | EvaluateManagedBackingFieldsNameofAnalyzer \ No newline at end of file +CSLA0023 | Refactoring | Info | EvaluateManagedBackingFieldsNameofAnalyzer +CSLA0024 | Usage | Warning | TypeWithOperationsShouldBePartialAnalyzer +CSLA0025 | Usage | Warning | UseGeneratedDataPortalExtensionAnalyzer \ No newline at end of file diff --git a/Source/Csla.Analyzers/Csla.Analyzers/Constants.cs b/Source/Csla.Analyzers/Csla.Analyzers/Constants.cs index da07f4d178..8e142e79f4 100644 --- a/Source/Csla.Analyzers/Csla.Analyzers/Constants.cs +++ b/Source/Csla.Analyzers/Csla.Analyzers/Constants.cs @@ -98,6 +98,14 @@ public static class AnalyzerIdentifiers /// /// public const string EvaluateManagedBackingFieldsNameof = "CSLA0023"; + /// + /// + /// + public const string TypeWithOperationsShouldBePartial = "CSLA0024"; + /// + /// + /// + public const string UseGeneratedDataPortalExtension = "CSLA0025"; } diff --git a/Source/Csla.Analyzers/Csla.Analyzers/Properties/Resources.Designer.cs b/Source/Csla.Analyzers/Csla.Analyzers/Properties/Resources.Designer.cs index 5556a12d37..3e46d186d7 100644 --- a/Source/Csla.Analyzers/Csla.Analyzers/Properties/Resources.Designer.cs +++ b/Source/Csla.Analyzers/Csla.Analyzers/Properties/Resources.Designer.cs @@ -549,5 +549,50 @@ internal static string Shared_AddAttributeDescription { return ResourceManager.GetString("Shared_AddAttributeDescription", resourceCulture); } } + + /// + /// Looks up a localized string similar to Make type partial. + /// + internal static string TypeWithOperationsShouldBePartial_AddPartialDescription { + get { + return ResourceManager.GetString("TypeWithOperationsShouldBePartial_AddPartialDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type '{0}' has data portal operation methods and should be declared partial so the CSLA source generator can implement its operations interface. + /// + internal static string TypeWithOperationsShouldBePartial_Message { + get { + return ResourceManager.GetString("TypeWithOperationsShouldBePartial_Message", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type with data portal operation methods should be partial. + /// + internal static string TypeWithOperationsShouldBePartial_Title { + get { + return ResourceManager.GetString("TypeWithOperationsShouldBePartial_Title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use the generated data portal extension method for '{0}' instead of calling '{1}' with untyped criteria. + /// + internal static string UseGeneratedDataPortalExtension_Message { + get { + return ResourceManager.GetString("UseGeneratedDataPortalExtension_Message", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use generated data portal extension method. + /// + internal static string UseGeneratedDataPortalExtension_Title { + get { + return ResourceManager.GetString("UseGeneratedDataPortalExtension_Title", resourceCulture); + } + } } } diff --git a/Source/Csla.Analyzers/Csla.Analyzers/Properties/Resources.resx b/Source/Csla.Analyzers/Csla.Analyzers/Properties/Resources.resx index 031868b675..5f2cbf3ce5 100644 --- a/Source/Csla.Analyzers/Csla.Analyzers/Properties/Resources.resx +++ b/Source/Csla.Analyzers/Csla.Analyzers/Properties/Resources.resx @@ -279,4 +279,19 @@ Authorization rules should be declared as static methods + + Type with data portal operation methods should be partial + + + Type '{0}' has data portal operation methods and should be declared partial so the CSLA source generator can implement its operations interface + + + Make type partial + + + Use generated data portal extension method + + + Use the generated data portal extension method for '{0}' instead of calling '{1}' with untyped criteria + \ No newline at end of file diff --git a/Source/Csla.Analyzers/Csla.Analyzers/TypeWithOperationsShouldBePartialAddPartialCodeFix.cs b/Source/Csla.Analyzers/Csla.Analyzers/TypeWithOperationsShouldBePartialAddPartialCodeFix.cs new file mode 100644 index 0000000000..e832a143f3 --- /dev/null +++ b/Source/Csla.Analyzers/Csla.Analyzers/TypeWithOperationsShouldBePartialAddPartialCodeFix.cs @@ -0,0 +1,86 @@ +using System.Collections.Immutable; +using System.Composition; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Csla.Analyzers +{ + /// + /// Adds the partial modifier to a type with data portal + /// operation methods and to all of its containing types. + /// + [ExportCodeFixProvider(LanguageNames.CSharp)] + [Shared] + public sealed class TypeWithOperationsShouldBePartialAddPartialCodeFix + : CodeFixProvider + { + /// + /// + /// + public override ImmutableArray FixableDiagnosticIds => [Constants.AnalyzerIdentifiers.TypeWithOperationsShouldBePartial]; + + /// + /// + /// + public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + + /// + /// + /// + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + if (root is null) + { + return; + } + + context.CancellationToken.ThrowIfCancellationRequested(); + + var diagnostic = context.Diagnostics.First(); + var classNode = root.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf(); + if (classNode is null) + { + return; + } + + var nonPartialNodes = TypeWithOperationsShouldBePartialAnalyzer.GetNonPartialTypeDeclarations(classNode); + if (nonPartialNodes.Count == 0) + { + return; + } + + context.CancellationToken.ThrowIfCancellationRequested(); + + var newRoot = root.ReplaceNodes(nonPartialNodes, (_, rewritten) => AddPartialModifier(rewritten)); + var description = TypeWithOperationsShouldBePartialAddPartialCodeFixConstants.AddPartialDescription; + + context.RegisterCodeFix( + CodeAction.Create(description, + _ => Task.FromResult(context.Document.WithSyntaxRoot(newRoot)), description), diagnostic); + } + + private static TypeDeclarationSyntax AddPartialModifier(TypeDeclarationSyntax node) + { + if (node.Modifiers.Count == 0) + { + // No modifiers: the partial keyword takes over the leading trivia + // (indentation, comments) of the type keyword. + var keyword = node.Keyword; + var partialToken = SyntaxFactory.Token( + keyword.LeadingTrivia, SyntaxKind.PartialKeyword, SyntaxFactory.TriviaList(SyntaxFactory.Space)); + return node + .WithKeyword(keyword.WithLeadingTrivia(SyntaxFactory.TriviaList())) + .WithModifiers(SyntaxFactory.TokenList(partialToken)); + } + + // partial must be the last modifier, immediately before the type keyword. + var partialModifier = SyntaxFactory.Token(SyntaxKind.PartialKeyword) + .WithTrailingTrivia(SyntaxFactory.Space); + return node.WithModifiers(node.Modifiers.Add(partialModifier)); + } + } +} diff --git a/Source/Csla.Analyzers/Csla.Analyzers/TypeWithOperationsShouldBePartialAnalyzer.cs b/Source/Csla.Analyzers/Csla.Analyzers/TypeWithOperationsShouldBePartialAnalyzer.cs new file mode 100644 index 0000000000..71cdc854df --- /dev/null +++ b/Source/Csla.Analyzers/Csla.Analyzers/TypeWithOperationsShouldBePartialAnalyzer.cs @@ -0,0 +1,143 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Immutable; + +namespace Csla.Analyzers +{ + /// + /// Reports classes that declare data portal operation methods + /// (methods with [Create], [Fetch], etc.) when the + /// class or any of its containing types is not partial. + /// The CSLA source generator needs to add a partial declaration + /// to such types to implement their operations interface. + /// + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class TypeWithOperationsShouldBePartialAnalyzer + : DiagnosticAnalyzer + { + private static readonly string[] OperationAttributeMetadataNames = + [ + "Csla.CreateAttribute", + "Csla.FetchAttribute", + "Csla.InsertAttribute", + "Csla.UpdateAttribute", + "Csla.ExecuteAttribute", + "Csla.DeleteAttribute", + "Csla.DeleteSelfAttribute", + "Csla.CreateChildAttribute", + "Csla.FetchChildAttribute", + "Csla.InsertChildAttribute", + "Csla.UpdateChildAttribute", + "Csla.DeleteSelfChildAttribute", + "Csla.ExecuteChildAttribute" + ]; + + private static readonly DiagnosticDescriptor shouldBePartialRule = + new( + Constants.AnalyzerIdentifiers.TypeWithOperationsShouldBePartial, TypeWithOperationsShouldBePartialAnalyzerConstants.Title, + TypeWithOperationsShouldBePartialAnalyzerConstants.Message, Constants.Categories.Usage, + DiagnosticSeverity.Warning, true, + helpLinkUri: HelpUrlBuilder.Build( + Constants.AnalyzerIdentifiers.TypeWithOperationsShouldBePartial, nameof(TypeWithOperationsShouldBePartialAnalyzer))); + + /// + /// + /// + public override ImmutableArray SupportedDiagnostics => [shouldBePartialRule]; + + /// + /// + /// + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterCompilationStartAction(compilationContext => + { + var operationAttributes = new HashSet(SymbolEqualityComparer.Default); + foreach (var metadataName in OperationAttributeMetadataNames) + { + var attributeType = compilationContext.Compilation.GetTypeByMetadataName(metadataName); + if (attributeType is not null) + { + operationAttributes.Add(attributeType); + } + } + + if (operationAttributes.Count == 0) + { + return; + } + + compilationContext.RegisterSymbolAction( + symbolContext => AnalyzeNamedType(symbolContext, operationAttributes), SymbolKind.NamedType); + }); + } + + private static void AnalyzeNamedType(SymbolAnalysisContext context, HashSet operationAttributes) + { + var typeSymbol = (INamedTypeSymbol)context.Symbol; + if (typeSymbol.TypeKind != TypeKind.Class || typeSymbol.IsRecord) + { + return; + } + + if (!HasOperationMethods(typeSymbol, operationAttributes)) + { + return; + } + + foreach (var reference in typeSymbol.DeclaringSyntaxReferences) + { + context.CancellationToken.ThrowIfCancellationRequested(); + + if (reference.GetSyntax(context.CancellationToken) is ClassDeclarationSyntax classNode && + GetNonPartialTypeDeclarations(classNode).Count > 0) + { + context.ReportDiagnostic(Diagnostic.Create( + shouldBePartialRule, classNode.Identifier.GetLocation(), typeSymbol.Name)); + return; + } + } + } + + private static bool HasOperationMethods(INamedTypeSymbol typeSymbol, HashSet operationAttributes) + { + foreach (var member in typeSymbol.GetMembers()) + { + if (member is IMethodSymbol method) + { + foreach (var attribute in method.GetAttributes()) + { + if (attribute.AttributeClass is not null && operationAttributes.Contains(attribute.AttributeClass)) + { + return true; + } + } + } + } + + return false; + } + + /// + /// Gets the given type declaration and all of its containing + /// type declarations that are not declared partial. + /// + internal static List GetNonPartialTypeDeclarations(TypeDeclarationSyntax typeNode) + { + var result = new List(); + foreach (var node in typeNode.AncestorsAndSelf().OfType()) + { + if (!node.Modifiers.Any(SyntaxKind.PartialKeyword)) + { + result.Add(node); + } + } + + return result; + } + } +} diff --git a/Source/Csla.Analyzers/Csla.Analyzers/TypeWithOperationsShouldBePartialAnalyzerConstants.cs b/Source/Csla.Analyzers/Csla.Analyzers/TypeWithOperationsShouldBePartialAnalyzerConstants.cs new file mode 100644 index 0000000000..c0bc4d1e2b --- /dev/null +++ b/Source/Csla.Analyzers/Csla.Analyzers/TypeWithOperationsShouldBePartialAnalyzerConstants.cs @@ -0,0 +1,31 @@ +using Csla.Analyzers.Properties; +using Microsoft.CodeAnalysis; + +namespace Csla.Analyzers +{ + /// + /// + /// + public static class TypeWithOperationsShouldBePartialAnalyzerConstants + { + /// + /// + /// + public static readonly LocalizableResourceString Title = new(nameof(Resources.TypeWithOperationsShouldBePartial_Title), Resources.ResourceManager, typeof(Resources)); + /// + /// + /// + public static readonly LocalizableResourceString Message = new(nameof(Resources.TypeWithOperationsShouldBePartial_Message), Resources.ResourceManager, typeof(Resources)); + } + + /// + /// + /// + public static class TypeWithOperationsShouldBePartialAddPartialCodeFixConstants + { + /// + /// + /// + public static string AddPartialDescription => Resources.TypeWithOperationsShouldBePartial_AddPartialDescription; + } +} diff --git a/Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzer.cs b/Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzer.cs new file mode 100644 index 0000000000..3be72c3b6a --- /dev/null +++ b/Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzer.cs @@ -0,0 +1,168 @@ +// Portions of this analyzer are adapted from the NotDataPortalExtensionMethodUsedAnalyzer +// in Csla.DataPortalExtensions (https://github.com/StefanOssendorf/Csla.DataPortalExtensions), +// Copyright (c) Stefan Ossendorf, licensed under the MIT License. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; +using System.Collections.Immutable; + +namespace Csla.Analyzers +{ + /// + /// Reports calls to the untyped, criteria-based methods of + /// IDataPortal<T>, IChildDataPortal<T>, and + /// DataPortal<T> when T is marked with + /// [DataPortalExtensions], meaning strongly typed extension + /// methods are generated and should be used instead. + /// + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class UseGeneratedDataPortalExtensionAnalyzer + : DiagnosticAnalyzer + { + private static readonly DiagnosticDescriptor useExtensionRule = + new( + Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension, UseGeneratedDataPortalExtensionAnalyzerConstants.Title, + UseGeneratedDataPortalExtensionAnalyzerConstants.Message, Constants.Categories.Usage, + DiagnosticSeverity.Warning, true, + helpLinkUri: HelpUrlBuilder.Build( + Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension, nameof(UseGeneratedDataPortalExtensionAnalyzer))); + + /// + /// + /// + public override ImmutableArray SupportedDiagnostics => [useExtensionRule]; + + /// + /// + /// + public override void Initialize(AnalysisContext context) + { + // Generated code must be skipped: the generated extension methods + // themselves call the untyped data portal methods. + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterCompilationStartAction(compilationContext => + { + var compilation = compilationContext.Compilation; + var extensionsAttribute = compilation.GetTypeByMetadataName("Csla.DataPortalExtensionsAttribute"); + if (extensionsAttribute is null) + { + return; + } + + var symbols = new KnownSymbols( + extensionsAttribute, + compilation.GetTypeByMetadataName("Csla.IDataPortal`1"), + compilation.GetTypeByMetadataName("Csla.IChildDataPortal`1"), + compilation.GetTypeByMetadataName("Csla.DataPortal`1")); + + compilationContext.RegisterOperationAction( + operationContext => AnalyzeInvocation(operationContext, symbols), OperationKind.Invocation); + }); + } + + private static void AnalyzeInvocation(OperationAnalysisContext context, KnownSymbols symbols) + { + var invocation = (IInvocationOperation)context.Operation; + var method = invocation.TargetMethod; + var containingType = method.ContainingType; + if (containingType is null || containingType.TypeArguments.Length != 1) + { + return; + } + + var definition = containingType.OriginalDefinition; + var isDataPortalClass = SymbolEqualityComparer.Default.Equals(definition, symbols.DataPortal); + var isRootPortal = isDataPortalClass || SymbolEqualityComparer.Default.Equals(definition, symbols.IDataPortal); + var isChildPortal = isDataPortalClass || SymbolEqualityComparer.Default.Equals(definition, symbols.IChildDataPortal); + if (!isRootPortal && !isChildPortal) + { + return; + } + + if (!IsCriteriaMethod(method, isRootPortal, isChildPortal)) + { + return; + } + + context.CancellationToken.ThrowIfCancellationRequested(); + + if (containingType.TypeArguments[0] is not INamedTypeSymbol businessType || + !HasDataPortalExtensionsAttribute(businessType, symbols.DataPortalExtensionsAttribute)) + { + return; + } + + context.ReportDiagnostic(Diagnostic.Create( + useExtensionRule, GetLocation(invocation), businessType.Name, method.Name)); + } + + private static bool IsCriteriaMethod(IMethodSymbol method, bool isRootPortal, bool isChildPortal) + { + switch (method.Name) + { + case "Create": + case "CreateAsync": + case "Fetch": + case "FetchAsync": + case "Delete": + case "DeleteAsync": + return isRootPortal; + case "Execute": + case "ExecuteAsync": + // Only the params object[] criteria overload, not Execute(T command) + return isRootPortal && method.Parameters.Length == 1 && method.Parameters[0].IsParams; + case "CreateChild": + case "CreateChildAsync": + case "FetchChild": + case "FetchChildAsync": + return isChildPortal; + default: + return false; + } + } + + private static bool HasDataPortalExtensionsAttribute(INamedTypeSymbol businessType, INamedTypeSymbol attributeType) + { + foreach (var attribute in businessType.GetAttributes()) + { + if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, attributeType)) + { + return true; + } + } + + return false; + } + + private static Location GetLocation(IInvocationOperation invocation) + { + if (invocation.Syntax is InvocationExpressionSyntax invocationSyntax) + { + switch (invocationSyntax.Expression) + { + case MemberAccessExpressionSyntax memberAccess: + return memberAccess.Name.GetLocation(); + case MemberBindingExpressionSyntax memberBinding: + return memberBinding.Name.GetLocation(); + } + } + + return invocation.Syntax.GetLocation(); + } + + private sealed class KnownSymbols( + INamedTypeSymbol dataPortalExtensionsAttribute, + INamedTypeSymbol? iDataPortal, + INamedTypeSymbol? iChildDataPortal, + INamedTypeSymbol? dataPortal) + { + public INamedTypeSymbol DataPortalExtensionsAttribute { get; } = dataPortalExtensionsAttribute; + public INamedTypeSymbol? IDataPortal { get; } = iDataPortal; + public INamedTypeSymbol? IChildDataPortal { get; } = iChildDataPortal; + public INamedTypeSymbol? DataPortal { get; } = dataPortal; + } + } +} diff --git a/Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzerConstants.cs b/Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzerConstants.cs new file mode 100644 index 0000000000..8087f46173 --- /dev/null +++ b/Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzerConstants.cs @@ -0,0 +1,20 @@ +using Csla.Analyzers.Properties; +using Microsoft.CodeAnalysis; + +namespace Csla.Analyzers +{ + /// + /// + /// + public static class UseGeneratedDataPortalExtensionAnalyzerConstants + { + /// + /// + /// + public static readonly LocalizableResourceString Title = new(nameof(Resources.UseGeneratedDataPortalExtension_Title), Resources.ResourceManager, typeof(Resources)); + /// + /// + /// + public static readonly LocalizableResourceString Message = new(nameof(Resources.UseGeneratedDataPortalExtension_Message), Resources.ResourceManager, typeof(Resources)); + } +} diff --git a/Source/tests/Csla.Analyzers.Tests/TypeWithOperationsShouldBePartialAddPartialCodeFixTests.cs b/Source/tests/Csla.Analyzers.Tests/TypeWithOperationsShouldBePartialAddPartialCodeFixTests.cs new file mode 100644 index 0000000000..31b20d0c6a --- /dev/null +++ b/Source/tests/Csla.Analyzers.Tests/TypeWithOperationsShouldBePartialAddPartialCodeFixTests.cs @@ -0,0 +1,183 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Collections.Immutable; + +namespace Csla.Analyzers.Tests +{ + [TestClass] + public sealed class TypeWithOperationsShouldBePartialAddPartialCodeFixTests + { + [TestMethod] + public void VerifyGetFixableDiagnosticIds() + { + var fix = new TypeWithOperationsShouldBePartialAddPartialCodeFix(); + var ids = fix.FixableDiagnosticIds.ToList(); + + Assert.AreEqual(1, ids.Count, nameof(ids.Count)); + Assert.AreEqual(ids[0], Constants.AnalyzerIdentifiers.TypeWithOperationsShouldBePartial, + nameof(Constants.AnalyzerIdentifiers.TypeWithOperationsShouldBePartial)); + } + + [TestMethod] + public async Task VerifyGetFixesWhenClassIsNotPartial() + { + var code = + """ + using Csla; + using System; + + [Serializable] + public sealed class A : BusinessBase + { + [Fetch] + private void Fetch(int id) { } + } + """; + var expected = + """ + using Csla; + using System; + + [Serializable] + public sealed partial class A : BusinessBase + { + [Fetch] + private void Fetch(int id) { } + } + """; + await VerifyFixAsync(code, expected); + } + + [TestMethod] + public async Task VerifyGetFixesWhenNestedClassAndContainingTypesAreNotPartial() + { + var code = + """ + using Csla; + using System; + + class Outer + { + public static partial class Middle + { + internal static class Inner + { + /// + /// Business class + /// + [Serializable] + public abstract class A : BusinessBase + where T : A + { + [Fetch] + protected void Fetch(int id) { } + } + } + } + } + """; + var expected = + """ + using Csla; + using System; + + partial class Outer + { + public static partial class Middle + { + internal static partial class Inner + { + /// + /// Business class + /// + [Serializable] + public abstract partial class A : BusinessBase + where T : A + { + [Fetch] + protected void Fetch(int id) { } + } + } + } + } + """; + await VerifyFixAsync(code, expected); + } + + [TestMethod] + public async Task VerifyGetFixesWhenClassHasNoModifiers() + { + var code = + """ + using Csla; + using System; + + namespace N + { + /// + /// Business class + /// + class A : BusinessBase + { + [Fetch] + private void Fetch(int id) { } + } + } + """; + var expected = + """ + using Csla; + using System; + + namespace N + { + /// + /// Business class + /// + partial class A : BusinessBase + { + [Fetch] + private void Fetch(int id) { } + } + } + """; + await VerifyFixAsync(code, expected); + } + + private static async Task VerifyFixAsync(string code, string expected) + { + var document = TestHelpers.Create(code); + var diagnostics = await TestHelpers.GetDiagnosticsAsync(code, new TypeWithOperationsShouldBePartialAnalyzer()); + Assert.AreEqual(1, diagnostics.Count, nameof(diagnostics.Count)); + + var actions = new List(); + var codeActionRegistration = new Action>( + (a, _) => { actions.Add(a); }); + + var fix = new TypeWithOperationsShouldBePartialAddPartialCodeFix(); + var codeFixContext = new CodeFixContext(document, diagnostics[0], + codeActionRegistration, new CancellationToken(false)); + await fix.RegisterCodeFixesAsync(codeFixContext); + + Assert.AreEqual(1, actions.Count); + + await TestHelpers.VerifyChangesAsync(actions, + TypeWithOperationsShouldBePartialAddPartialCodeFixConstants.AddPartialDescription, document, + (model, newRoot) => + { + Assert.AreEqual(expected, newRoot.ToFullString()); + foreach (var typeNode in newRoot.DescendantNodes().OfType()) + { + Assert.IsTrue(typeNode.Modifiers.Any(SyntaxKind.PartialKeyword), typeNode.Identifier.Text); + } + }); + + var fixedDiagnostics = await TestHelpers.GetDiagnosticsAsync(expected, new TypeWithOperationsShouldBePartialAnalyzer()); + Assert.AreEqual(0, fixedDiagnostics.Count, nameof(fixedDiagnostics.Count)); + } + } +} diff --git a/Source/tests/Csla.Analyzers.Tests/TypeWithOperationsShouldBePartialAnalyzerTests.cs b/Source/tests/Csla.Analyzers.Tests/TypeWithOperationsShouldBePartialAnalyzerTests.cs new file mode 100644 index 0000000000..5e68fda82e --- /dev/null +++ b/Source/tests/Csla.Analyzers.Tests/TypeWithOperationsShouldBePartialAnalyzerTests.cs @@ -0,0 +1,264 @@ +using Microsoft.CodeAnalysis; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Csla.Analyzers.Tests +{ + [TestClass] + public sealed class TypeWithOperationsShouldBePartialAnalyzerTests + { + [TestMethod] + public void VerifySupportedDiagnostics() + { + var analyzer = new TypeWithOperationsShouldBePartialAnalyzer(); + var diagnostics = analyzer.SupportedDiagnostics; + Assert.AreEqual(1, diagnostics.Length); + + var diagnostic = diagnostics[0]; + Assert.AreEqual(Constants.AnalyzerIdentifiers.TypeWithOperationsShouldBePartial, diagnostic.Id, + nameof(DiagnosticDescriptor.Id)); + Assert.AreEqual("Type with data portal operation methods should be partial", diagnostic.Title.ToString(), + nameof(DiagnosticDescriptor.Title)); + Assert.AreEqual("Type '{0}' has data portal operation methods and should be declared partial so the CSLA source generator can implement its operations interface", + diagnostic.MessageFormat.ToString(), + nameof(DiagnosticDescriptor.MessageFormat)); + Assert.AreEqual(Constants.Categories.Usage, diagnostic.Category, + nameof(DiagnosticDescriptor.Category)); + Assert.AreEqual(DiagnosticSeverity.Warning, diagnostic.DefaultSeverity, + nameof(DiagnosticDescriptor.DefaultSeverity)); + Assert.AreEqual(HelpUrlBuilder.Build(Constants.AnalyzerIdentifiers.TypeWithOperationsShouldBePartial, nameof(TypeWithOperationsShouldBePartialAnalyzer)), + diagnostic.HelpLinkUri, + nameof(DiagnosticDescriptor.HelpLinkUri)); + } + + [TestMethod] + public async Task AnalyzeWhenClassIsNotPartialAndHasOperationMethod() + { + var code = + """ + using Csla; + using System; + + [Serializable] + public class A : BusinessBase + { + [Fetch] + private void Fetch(int id) { } + } + """; + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.TypeWithOperationsShouldBePartial], + diagnostics => + { + Assert.AreEqual("Type 'A' has data portal operation methods and should be declared partial so the CSLA source generator can implement its operations interface", + diagnostics[0].GetMessage()); + var location = diagnostics[0].Location; + Assert.AreEqual("A", location.SourceTree.GetText().ToString(location.SourceSpan)); + }); + } + + [TestMethod] + public async Task AnalyzeWhenClassIsNotPartialAndHasChildOperationMethod() + { + var code = + """ + using Csla; + using System; + + [Serializable] + public class A : BusinessBase + { + [UpdateChild] + private void Child_Update() { } + } + """; + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.TypeWithOperationsShouldBePartial]); + } + + [TestMethod] + public async Task AnalyzeWhenClassIsNotPartialAndHasMultipleOperationMethods() + { + var code = + """ + using Csla; + using System; + + [Serializable] + public class A : BusinessBase + { + [Create] + private void Create() { } + + [Fetch] + private void Fetch(int id) { } + + [Insert] + private void Insert() { } + } + """; + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.TypeWithOperationsShouldBePartial]); + } + + [TestMethod] + public async Task AnalyzeWhenClassIsPartial() + { + var code = + """ + using Csla; + using System; + + [Serializable] + public partial class A : BusinessBase + { + [Fetch] + private void Fetch(int id) { } + } + """; + await TestHelpers.RunAnalysisAsync(code, []); + } + + [TestMethod] + public async Task AnalyzeWhenClassHasNoOperationMethods() + { + var code = + """ + using Csla; + using System; + + [Serializable] + public class A : BusinessBase + { + private void DataPortal_Fetch(int id) { } + + public void DoWork() { } + } + """; + await TestHelpers.RunAnalysisAsync(code, []); + } + + [TestMethod] + public async Task AnalyzeWhenNestedClassIsPartialButContainingClassIsNot() + { + var code = + """ + using Csla; + using System; + + public class Outer + { + [Serializable] + public partial class A : BusinessBase + { + [Fetch] + private void Fetch(int id) { } + } + } + """; + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.TypeWithOperationsShouldBePartial], + diagnostics => Assert.IsTrue(diagnostics[0].GetMessage().Contains("'A'"))); + } + + [TestMethod] + public async Task AnalyzeWhenNestedClassAndContainingClassesArePartial() + { + var code = + """ + using Csla; + using System; + + public partial class Outer + { + public static partial class Middle + { + [Serializable] + public partial class A : BusinessBase + { + [Fetch] + private void Fetch(int id) { } + } + } + } + """; + await TestHelpers.RunAnalysisAsync(code, []); + } + + [TestMethod] + public async Task AnalyzeWhenNestedClassAndContainingClassAreNotPartial() + { + var code = + """ + using Csla; + using System; + + public class Outer + { + [Serializable] + public class A : BusinessBase + { + [Fetch] + private void Fetch(int id) { } + } + } + """; + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.TypeWithOperationsShouldBePartial]); + } + + [TestMethod] + public async Task AnalyzeWhenClassIsAbstract() + { + var code = + """ + using Csla; + using System; + + [Serializable] + public abstract class A : BusinessBase + where T : A + { + [Fetch] + protected void Fetch(int id) { } + } + """; + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.TypeWithOperationsShouldBePartial]); + } + + [TestMethod] + public async Task AnalyzeWhenTypeIsRecord() + { + var code = + """ + using Csla; + + public record A + { + [Fetch] + private void Fetch(int id) { } + } + """; + await TestHelpers.RunAnalysisAsync(code, []); + } + + [TestMethod] + public async Task AnalyzeWhenCodeIsGenerated() + { + var code = + """ + // + using Csla; + using System; + + [Serializable] + public class A : BusinessBase + { + [Fetch] + private void Fetch(int id) { } + } + """; + await TestHelpers.RunAnalysisAsync(code, []); + } + } +} diff --git a/Source/tests/Csla.Analyzers.Tests/UseGeneratedDataPortalExtensionAnalyzerTests.cs b/Source/tests/Csla.Analyzers.Tests/UseGeneratedDataPortalExtensionAnalyzerTests.cs new file mode 100644 index 0000000000..7fc648703b --- /dev/null +++ b/Source/tests/Csla.Analyzers.Tests/UseGeneratedDataPortalExtensionAnalyzerTests.cs @@ -0,0 +1,246 @@ +using Microsoft.CodeAnalysis; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Csla.Analyzers.Tests +{ + [TestClass] + public sealed class UseGeneratedDataPortalExtensionAnalyzerTests + { + private const string BusinessTypes = + """ + [DataPortalExtensions] + [Serializable] + public partial class A : BusinessBase + { + [Create] + private void Create() { } + + [Fetch] + private void Fetch(int id) { } + + [Delete] + private void Delete(int id) { } + + [FetchChild] + private void FetchChild(int id) { } + + [CreateChild] + private void CreateChild() { } + } + + [DataPortalExtensions] + [Serializable] + public partial class C : CommandBase + { + [Execute] + private void Execute(int id) { } + } + + [Serializable] + public partial class B : BusinessBase + { + [Fetch] + private void Fetch(int id) { } + } + """; + + private static string CreateCode(string body, string header = "") => + $$""" + {{header}} + using Csla; + using System; + using System.Threading.Tasks; + + {{BusinessTypes}} + + public class Consumer + { + public async Task Run(IDataPortal portal, IChildDataPortal childPortal, + DataPortal concretePortal, IDataPortal otherPortal, IDataPortal commandPortal, C command) + { + {{body}} + await Task.CompletedTask; + } + } + """; + + [TestMethod] + public void VerifySupportedDiagnostics() + { + var analyzer = new UseGeneratedDataPortalExtensionAnalyzer(); + var diagnostics = analyzer.SupportedDiagnostics; + Assert.AreEqual(1, diagnostics.Length); + + var diagnostic = diagnostics[0]; + Assert.AreEqual(Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension, diagnostic.Id, + nameof(DiagnosticDescriptor.Id)); + Assert.AreEqual("Use generated data portal extension method", diagnostic.Title.ToString(), + nameof(DiagnosticDescriptor.Title)); + Assert.AreEqual("Use the generated data portal extension method for '{0}' instead of calling '{1}' with untyped criteria", + diagnostic.MessageFormat.ToString(), + nameof(DiagnosticDescriptor.MessageFormat)); + Assert.AreEqual(Constants.Categories.Usage, diagnostic.Category, + nameof(DiagnosticDescriptor.Category)); + Assert.AreEqual(DiagnosticSeverity.Warning, diagnostic.DefaultSeverity, + nameof(DiagnosticDescriptor.DefaultSeverity)); + Assert.AreEqual(HelpUrlBuilder.Build(Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension, nameof(UseGeneratedDataPortalExtensionAnalyzer)), + diagnostic.HelpLinkUri, + nameof(DiagnosticDescriptor.HelpLinkUri)); + } + + [TestMethod] + public async Task AnalyzeWhenFetchAsyncIsCalledOnDataPortal() + { + var code = CreateCode("await portal.FetchAsync(1);"); + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension], + diagnostics => + { + Assert.AreEqual("Use the generated data portal extension method for 'A' instead of calling 'FetchAsync' with untyped criteria", + diagnostics[0].GetMessage()); + var location = diagnostics[0].Location; + Assert.AreEqual("FetchAsync", location.SourceTree.GetText().ToString(location.SourceSpan)); + }); + } + + [TestMethod] + public async Task AnalyzeWhenFetchIsCalledOnDataPortal() + { + var code = CreateCode("portal.Fetch(1);"); + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension]); + } + + [TestMethod] + public async Task AnalyzeWhenCreateIsCalledOnDataPortal() + { + var code = CreateCode("portal.Create();"); + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension]); + } + + [TestMethod] + public async Task AnalyzeWhenCreateAsyncIsCalledOnDataPortal() + { + var code = CreateCode("await portal.CreateAsync();"); + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension]); + } + + [TestMethod] + public async Task AnalyzeWhenDeleteIsCalledOnDataPortal() + { + var code = CreateCode("portal.Delete(1);"); + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension]); + } + + [TestMethod] + public async Task AnalyzeWhenDeleteAsyncIsCalledOnDataPortal() + { + var code = CreateCode("await portal.DeleteAsync(1);"); + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension]); + } + + [TestMethod] + public async Task AnalyzeWhenExecuteWithCriteriaIsCalledOnDataPortal() + { + var code = CreateCode("commandPortal.Execute(1);"); + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension]); + } + + [TestMethod] + public async Task AnalyzeWhenExecuteAsyncWithCriteriaIsCalledOnDataPortal() + { + var code = CreateCode("await commandPortal.ExecuteAsync(1);"); + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension]); + } + + [TestMethod] + public async Task AnalyzeWhenExecuteWithCommandIsCalledOnDataPortal() + { + var code = CreateCode("commandPortal.Execute(command);"); + await TestHelpers.RunAnalysisAsync(code, []); + } + + [TestMethod] + public async Task AnalyzeWhenExecuteAsyncWithCommandIsCalledOnDataPortal() + { + var code = CreateCode("await commandPortal.ExecuteAsync(command);"); + await TestHelpers.RunAnalysisAsync(code, []); + } + + [TestMethod] + public async Task AnalyzeWhenFetchChildIsCalledOnChildDataPortal() + { + var code = CreateCode("childPortal.FetchChild(1);"); + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension], + diagnostics => Assert.AreEqual( + "Use the generated data portal extension method for 'A' instead of calling 'FetchChild' with untyped criteria", + diagnostics[0].GetMessage())); + } + + [TestMethod] + public async Task AnalyzeWhenCreateChildAsyncIsCalledOnChildDataPortal() + { + var code = CreateCode("await childPortal.CreateChildAsync();"); + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension]); + } + + [TestMethod] + public async Task AnalyzeWhenMethodsAreCalledOnConcreteDataPortal() + { + var code = CreateCode( + """ + concretePortal.Fetch(1); + await concretePortal.FetchChildAsync(1); + """); + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension, Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension]); + } + + [TestMethod] + public async Task AnalyzeWhenBusinessTypeDoesNotHaveDataPortalExtensionsAttribute() + { + var code = CreateCode( + """ + await otherPortal.FetchAsync(1); + otherPortal.Fetch(1); + """); + await TestHelpers.RunAnalysisAsync(code, []); + } + + [TestMethod] + public async Task AnalyzeWhenUpdateIsCalledOnDataPortal() + { + var code = CreateCode( + """ + var item = await portal.CreateAsync(); + await portal.UpdateAsync(item); + portal.Update(item); + await concretePortal.UpdateAsync(item); + """); + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension], + diagnostics => Assert.IsTrue(diagnostics[0].GetMessage().Contains("'CreateAsync'"))); + } + + [TestMethod] + public async Task AnalyzeWhenCodeIsGenerated() + { + var code = CreateCode( + """ + await portal.FetchAsync(1); + childPortal.FetchChild(1); + commandPortal.Execute(1); + """, + "// "); + await TestHelpers.RunAnalysisAsync(code, []); + } + } +} diff --git a/docs/analyzers/CSLA0024-TypeWithOperationsShouldBePartialAnalyzer.md b/docs/analyzers/CSLA0024-TypeWithOperationsShouldBePartialAnalyzer.md new file mode 100644 index 0000000000..2a0d28a121 --- /dev/null +++ b/docs/analyzers/CSLA0024-TypeWithOperationsShouldBePartialAnalyzer.md @@ -0,0 +1,52 @@ +# Types with data portal operation methods should be partial. + +## Issue + +This analyzer is tripped if a class declares one or more data portal operation methods (methods marked with `[Create]`, `[Fetch]`, `[Insert]`, `[Update]`, `[Execute]`, `[Delete]`, `[DeleteSelf]`, `[CreateChild]`, `[FetchChild]`, `[InsertChild]`, `[UpdateChild]`, `[DeleteSelfChild]`, or `[ExecuteChild]`), and either the class itself or any of its containing types is not declared `partial`. For example: + +``` +using Csla; +using System; + +[Serializable] +public class Customer + : BusinessBase +{ + [Fetch] + private void Fetch(int id) { } +} +``` + +The CSLA source generator adds a `partial` declaration to each class that has data portal operation methods. That declaration implements an internal operations interface so the data portal can invoke the operation methods directly, without reflection. The generator can only do this when the class, and every type that contains it, is `partial`: + +``` +using Csla; +using System; + +[Serializable] +public partial class Customer + : BusinessBase +{ + [Fetch] + private void Fetch(int id) { } +} +``` + +This applies to abstract and generic classes as well. For nested classes, every containing type must also be `partial`: + +``` +public partial class Outer +{ + [Serializable] + public partial class Customer + : BusinessBase + { + [Fetch] + private void Fetch(int id) { } + } +} +``` + +## Code Fix + +A code fix is available for this analyzer. It will add the `partial` modifier to the class and to all of its containing types that are not already `partial`. diff --git a/docs/analyzers/CSLA0025-UseGeneratedDataPortalExtensionAnalyzer.md b/docs/analyzers/CSLA0025-UseGeneratedDataPortalExtensionAnalyzer.md new file mode 100644 index 0000000000..f459fd88db --- /dev/null +++ b/docs/analyzers/CSLA0025-UseGeneratedDataPortalExtensionAnalyzer.md @@ -0,0 +1,39 @@ +# Use the generated data portal extension method. + +## Issue + +When a business class is marked with `[DataPortalExtensions]`, the CSLA source generator creates strongly typed extension methods on `IDataPortal` and `IChildDataPortal` for each of the class's data portal operation methods. This analyzer is tripped when code calls the untyped, criteria-based data portal methods for such a type instead of the generated extension methods. For example: + +``` +using Csla; +using System; + +[DataPortalExtensions] +[Serializable] +public partial class Customer + : BusinessBase +{ + [Fetch] + private void Fetch(int id) { } +} + +public class CustomerService(IDataPortal portal) +{ + public Task GetCustomerAsync(int id) => portal.FetchAsync(id); +} +``` + +The untyped methods accept `params object[]` criteria, so mistakes in the number, order, or types of the arguments are only discovered at runtime. The generated extension methods have parameters that match the operation method, so the compiler verifies each call. + +The following methods are reported when the business type `T` has the `[DataPortalExtensions]` attribute: + +* On `IDataPortal` and `DataPortal`: `Create`, `CreateAsync`, `Fetch`, `FetchAsync`, `Delete`, `DeleteAsync`, and the criteria overloads of `Execute` and `ExecuteAsync` +* On `IChildDataPortal` and `DataPortal`: `CreateChild`, `CreateChildAsync`, `FetchChild`, and `FetchChildAsync` + +`Update`, `UpdateAsync`, `UpdateChild`, `UpdateChildAsync`, and `Execute`/`ExecuteAsync` called with a command object are not reported, because they do not take untyped criteria. + +Generated code is not analyzed, because the generated extension methods themselves call the untyped data portal methods. + +## Code Fix + +No code fix exists for this analyzer. Replace the call with the corresponding generated extension method. diff --git a/docs/analyzers/index.md b/docs/analyzers/index.md index f7f26bc995..3e2eddd696 100644 --- a/docs/analyzers/index.md +++ b/docs/analyzers/index.md @@ -21,4 +21,29 @@ While we try to make the analyzers as stable as we can, we may run into coding p Unfortunately, Visual Studio doesn't make it easy to get crash information on an analyzer. The best you can do is launch `devenv.exe` with the `/log` switch, and see if the log file contains any meaningful information. Also, please log an issue on the [CSLA site]((https://github.com/MarimerLLC/csla/issues)) if you do find problems with an analyzer, and we'll do our best to resolve the problem. ## Analyzer Proposals -If you have an idea for an analyzer that would be beneficial for developers using CSLA, please propose it in the [forums](https://github.com/MarimerLLC/cslaforum/issues), and tag it with "feature discussion". \ No newline at end of file +If you have an idea for an analyzer that would be beneficial for developers using CSLA, please propose it in the [forums](https://github.com/MarimerLLC/cslaforum/issues), and tag it with "feature discussion". +## Analyzer Reference +* [CSLA0001](CSLA0001-IsBusinessObjectSerializableAnalyzer.md) - CSLA business objects must be serializable +* [CSLA0002](CSLA0002-IsOperationMethodPublicAnalyzer.md) - CSLA operations should not be public +* [CSLA0003](CSLA0003-CheckConstructorsAnalyzer.md) - CSLA business objects must have a public constructor with no arguments +* [CSLA0004](CSLA0004-CheckConstructorsAnalyzer.md) - CSLA business objects should not have public constructors with parameters +* [CSLA0005](CSLA0005-FindSaveAssignmentIssueAnalyzer.md) - Do not ignore the result of `Save()` +* [CSLA0006](CSLA0006-FindSaveAssignmentIssueAnalyzer.md) - Do not ignore the result of `SaveAsync()` +* [CSLA0007](CSLA0007-EvaluatePropertiesForSimplicityAnalyzer.md) - Properties that use managed backing fields should only use Get/Set/Read/Load methods +* [CSLA0008](CSLA0008-EvaluateManagedBackingFieldsAnalayzer.md) - Managed backing fields must be public, static and read-only +* [CSLA0009](CSLA0009-IsOperationMethodPublicAnalyzer.md) - CSLA operations should not be public +* [CSLA0010](CSLA0010-FindOperationsWithNonSerializableArgumentsAnalyzer.md) - Operation argument types should be serializable +* [CSLA0011](CSLA0011-FindBusinessObjectCreationAnalyzer.md) - CSLA business objects should not be created outside of an `ObjectFactory` instance +* [CSLA0012](CSLA0012-FindOperationsWithIncorrectReturnTypesAnalyzer.md) - The return type from an operation should be either `void` or `Task` +* [CSLA0013](CSLA0013-DoesChildOperationHaveRunLocalAnalyzer.md) - Child operations should not have `[RunLocal]` +* [CSLA0014](CSLA0014-DoesOperationHaveAttributeAnalyzer.md) - Operations should have the appropriate operation attribute +* [CSLA0015](CSLA0015-EvaluateOperationAttributeUsageAnalyzer.md) - Operation attributes should be used correctly +* [CSLA0016](CSLA0016-AsynchronousBusinessRuleInheritingFromBusinessRuleAnalyzer.md) - Asynchronous business rules should derive from `BusinessRuleAsync` +* [CSLA0017](CSLA0017-BusinessRuleDoesNotUseAddMethodsOnContextAnalyzer.md) - Business rules should use at least one `Add()` method on the context +* [CSLA0018](CSLA0018-IsCompleteCalledInAsynchronousBusinessRuleAnalyzer.md) - `Complete()` should not be called in an asynchronous business rule +* [CSLA0019](CSLA0019-FindRefAndOutParametersInOperationsAnalyzer.md) - Operations should not have `ref` or `out` parameters +* [CSLA0020](CSLA0020-ObjectAuthorizationRulesAttributeMissing.md) - Object authorization rules configuration should be marked with attribute +* [CSLA0021](CSLA0021-ObjectAuthorizationRulesPublic.md) - Object authorization rules configuration should be declared as public +* [CSLA0022](CSLA0022-ObjectAuthorizationRulesStatic.md) - Object authorization rules configuration should be declared as static +* [CSLA0024](CSLA0024-TypeWithOperationsShouldBePartialAnalyzer.md) - Types with data portal operation methods should be partial +* [CSLA0025](CSLA0025-UseGeneratedDataPortalExtensionAnalyzer.md) - Use the generated data portal extension method From bf2cd1b83fac3a3ce066b8282a7d5b7d8203bf6d Mon Sep 17 00:00:00 2001 From: Rockford lhotka Date: Sun, 13 Sep 2026 18:29:55 -0500 Subject: [PATCH 04/10] #4359 Add pre-resolved data portal invokers and DataPortalExtensions generator - Add IDataPortalOperationInvoker and IChildDataPortalOperationInvoker, implemented by DataPortal, which take a generated operation name and RunLocal flag so the client skips reflection-based method lookup - Flow operation names through ChildDataPortal and DataPortalTarget for CreateChild/FetchChild named dispatch - Wrap a single covariant array criteria value so it is not split - Add the DataPortalExtensions generator (adapted from Stefan Ossendorf's MIT Csla.DataPortalExtensions) emitting sync and async extensions on IDataPortal/IChildDataPortal with fallback for custom portals - Add generator snapshot, stage caching, integration and name consistency tests; update release notes and docs Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Dn4aKN7kqcG78x1HBWApk3 --- .../DataPortalExtensionsBuilder.cs | 242 ++++++++ .../ExtensionTypeModel.cs | 61 ++ ...ncrementalDataPortalExtensionsGenerator.cs | 125 ++++ .../DataPortalOperationsDiagnostics.cs | 10 + .../OperationDiscovery.cs | 2 +- .../cs/AutoImplementProperties/readme.md | 3 +- Source/Csla/DataPortalT.cs | 178 +++++- Source/Csla/IDataPortalOperationInvoker.cs | 134 +++++ Source/Csla/Server/ChildDataPortal.cs | 95 ++- Source/Csla/Server/DataPortalTarget.cs | 29 +- .../DataPortalExtensionsGeneratorTests.cs | 381 ++++++++++++ ...sonEdit.DataPortalExtensions.g.verified.cs | 52 ++ ...ineItem.DataPortalExtensions.g.verified.cs | 52 ++ ...sonEdit.DataPortalExtensions.g.verified.cs | 32 + ...sonEdit.DataPortalExtensions.g.verified.cs | 32 + ...ratorTests.DuplicateExtension.verified.txt | 32 + ...Command.DataPortalExtensions.g.verified.cs | 32 + ...ratorTests.GenericTypeSkipped.verified.txt | 18 + ...sonEdit.DataPortalExtensions.g.verified.cs | 32 + ...onsGeneratorTests.HiddenNames.verified.txt | 60 ++ ...sonEdit.DataPortalExtensions.g.verified.cs | 32 + ...GeneratorTests.InvalidTargets.verified.txt | 32 + ...sonEdit.DataPortalExtensions.g.verified.cs | 32 + ...sonEdit.DataPortalExtensions.g.verified.cs | 32 + ...sonEdit.DataPortalExtensions.g.verified.cs | 32 + ...rTests.ParameterAccessibility.verified.txt | 17 + ...sonEdit.DataPortalExtensions.g.verified.cs | 52 ++ ...sonEdit.DataPortalExtensions.g.verified.cs | 93 +++ .../DataPortalOperationsTestHelper.cs | 9 +- .../DataPortal/DataPortalExtensionsTests.cs | 560 ++++++++++++++++++ .../csla.netcore.test.csproj | 1 + docs/code-generators.md | 2 +- releasenotes.md | 39 ++ 33 files changed, 2490 insertions(+), 45 deletions(-) create mode 100644 Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsBuilder.cs create mode 100644 Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/ExtensionTypeModel.cs create mode 100644 Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/IncrementalDataPortalExtensionsGenerator.cs create mode 100644 Source/Csla/IDataPortalOperationInvoker.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/DataPortalExtensionsGeneratorTests.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ArrayCriteria#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalExtensions.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.DefaultValues#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.DuplicateExtension#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.DuplicateExtension.verified.txt create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ExecuteCommand#TestApp.RecalculateCommand.DataPortalExtensions.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.GenericTypeSkipped.verified.txt create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.HiddenNames#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.HiddenNames.verified.txt create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InternalType#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidTargets.verified.txt create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.NestedType#TestApp.Outer.PersonEdit.DataPortalExtensions.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.NoDataPortalExtension#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ParameterAccessibility#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ParameterAccessibility.verified.txt create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.Prefix#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.RootOperations#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs create mode 100644 Source/tests/csla.netcore.test/DataPortal/DataPortalExtensionsTests.cs diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsBuilder.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsBuilder.cs new file mode 100644 index 0000000000..6b218c388a --- /dev/null +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsBuilder.cs @@ -0,0 +1,242 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// Builds strongly typed data portal extension methods +//----------------------------------------------------------------------- +// Portions adapted from Csla.DataPortalExtensions by Stefan Ossendorf +// (https://github.com/StefanOssendorf/Csla.DataPortalExtensions), +// licensed under the MIT License. Copyright (c) 2023 Stefan Ossendorf. +//----------------------------------------------------------------------- + +using System.CodeDom.Compiler; +using Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations; +using Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations.Models; + +namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalExtensions +{ + /// + /// Builds the static class containing the strongly typed data portal + /// extension methods for a business type. + /// + internal static class DataPortalExtensionsBuilder + { + private const string ReceiverName = "portal"; + + /// + /// Build the generated source and diagnostics for a business type. + /// Returns a null source when nothing is generated. + /// + public static (string? Source, List Diagnostics) Build(ExtensionGenerationModel model) + { + var type = model.Type; + var diagnostics = new List(); + + if (type.IsGeneric) + { + diagnostics.Add(Diagnostic(DataPortalOperationsDiagnostics.GenericExtensionsNotGeneratedId, type.Location, type.TypeName)); + return (null, diagnostics); + } + + var invalidReason = type switch + { + { IsCslaObject: false } => "the type does not implement Csla.Core.ICslaObject", + { IsAbstract: true } => "the type is abstract", + { Visibility: TypeVisibility.Private } => "the type is not accessible outside its containing type", + _ => null + }; + if (invalidReason is not null) + { + diagnostics.Add(Diagnostic(DataPortalOperationsDiagnostics.InvalidExtensionsTargetId, type.Location, type.TypeName, invalidReason)); + return (null, diagnostics); + } + + var methods = SelectMethods(model, diagnostics); + if (methods.Count == 0) + return (null, diagnostics); + + using var stringWriter = new StringWriter(); + var writer = new IndentedTextWriter(stringWriter, " "); + + writer.WriteLine("//"); + writer.WriteLine("#nullable enable"); + writer.WriteLineNoTabs(string.Empty); + + if (type.Namespace.Length > 0) + { + writer.WriteLine($"namespace {type.Namespace}"); + OpenBlock(writer); + } + + var classVisibility = type.Visibility == TypeVisibility.Public ? "public" : "internal"; + writer.WriteLine("/// "); + writer.WriteLine($"/// Strongly typed data portal extension methods for ."); + writer.WriteLine("/// "); + writer.WriteLine($"{classVisibility} static partial class {type.ExtensionClassName}"); + OpenBlock(writer); + + var isFirst = true; + foreach (var method in methods) + { + if (!isFirst) + writer.WriteLineNoTabs(string.Empty); + isFirst = false; + AppendMethod(writer, type, method); + } + + CloseBlock(writer); + + if (type.Namespace.Length > 0) + CloseBlock(writer); + + return (stringWriter.ToString(), diagnostics); + } + + private static List SelectMethods(ExtensionGenerationModel model, List diagnostics) + { + var type = model.Type; + var result = new List(); + var signatures = new HashSet(StringComparer.Ordinal); + var (_, collisions) = DataPortalOperationsBuilder.SelectNamedDispatchMethods(model.Methods); + + foreach (var method in model.Methods) + { + if (method.NoExtension || !method.CanDispatch) + continue; + + // Methods sharing an operation name differ only in injected parameters, + // so they would produce identical extension methods; the runtime's + // choice is reported by the operations generator. + if (collisions.Any(c => ReferenceEquals(c.Loser, method))) + continue; + + var criteria = method.CriteriaParameters.ToList(); + var inaccessible = criteria.FirstOrDefault(p => p.Visibility == TypeVisibility.Private); + if (inaccessible is not null) + { + diagnostics.Add(Diagnostic(DataPortalOperationsDiagnostics.InaccessibleParameterTypeId, method.Location, method.MethodDisplay, inaccessible.Name.TrimStart('@'), inaccessible.PatternTypeDisplay)); + continue; + } + + var baseName = method.MethodName.TrimStart('@'); + if (baseName.EndsWith("Async", StringComparison.Ordinal) && baseName.Length > "Async".Length) + baseName = baseName.Substring(0, baseName.Length - "Async".Length); + + var isChild = method.Kind is "CreateChild" or "FetchChild"; + var receiverInterface = isChild ? "IChildDataPortal" : "IDataPortal"; + var parameterTypes = string.Join(",", criteria.Select(p => p.PatternTypeDisplay + (p.IsNullableValueType ? "?" : string.Empty))); + + foreach (var isAsync in new[] { true, false }) + { + var name = type.Prefix + baseName + (isAsync ? "Async" : string.Empty); + if (type.HiddenNames.Contains(name)) + { + diagnostics.Add(Diagnostic(DataPortalOperationsDiagnostics.ExtensionNameHiddenId, method.Location, name, type.TypeName, $"{receiverInterface}.{name}")); + continue; + } + + if (!signatures.Add($"{receiverInterface}.{name}({parameterTypes})")) + { + diagnostics.Add(Diagnostic(DataPortalOperationsDiagnostics.DuplicateExtensionMethodId, method.Location, name, type.TypeName, method.MethodDisplay)); + continue; + } + + result.Add(new ExtensionMethod(method, name, isAsync, isChild)); + } + } + + return result; + } + + private static void AppendMethod(IndentedTextWriter writer, ExtensionTypeModel type, ExtensionMethod extension) + { + var method = extension.Method; + var criteria = method.CriteriaParameters.ToList(); + var isDelete = method.Kind == "Delete"; + var receiver = criteria.Any(p => p.Name == ReceiverName) ? "__" + ReceiverName : ReceiverName; + var visibility = type.Visibility == TypeVisibility.Public && criteria.All(p => p.Visibility == TypeVisibility.Public) + ? "public" + : "internal"; + + var returnType = (extension.IsAsync, isDelete) switch + { + (true, true) => "global::System.Threading.Tasks.Task", + (true, false) => $"global::System.Threading.Tasks.Task<{type.FullyQualifiedName}>", + (false, true) => "void", + (false, false) => type.FullyQualifiedName + }; + + var portalType = extension.IsChild + ? $"global::Csla.IChildDataPortal<{type.FullyQualifiedName}>" + : $"global::Csla.IDataPortal<{type.FullyQualifiedName}>"; + var invokerType = extension.IsChild + ? $"global::Csla.IChildDataPortalOperationInvoker<{type.FullyQualifiedName}>" + : $"global::Csla.IDataPortalOperationInvoker<{type.FullyQualifiedName}>"; + + var parameters = new List { $"this {portalType} {receiver}" }; + foreach (var parameter in criteria) + { + var declaration = $"{(parameter.IsParams ? "params " : string.Empty)}{parameter.TypeDisplay} {parameter.Name}"; + if (parameter.DefaultValueExpression is not null) + declaration += $" = {parameter.DefaultValueExpression}"; + parameters.Add(declaration); + } + + var criteriaArray = criteria.Count == 0 + ? "global::System.Array.Empty()" + : $"new object?[] {{ {string.Join(", ", criteria.Select(p => p.Name))} }}"; + + var portalMethod = method.Kind + (extension.IsAsync ? "Async" : string.Empty); + var invokerMethod = method.Kind + "ByOperation" + (extension.IsAsync ? "Async" : string.Empty); + var invokerArguments = extension.IsChild + ? $"\"{method.OperationName}\", {criteriaArray}" + : $"\"{method.OperationName}\", {(method.RunLocal ? "true" : "false")}, {criteriaArray}"; + var fallbackArguments = criteria.Count == 0 ? string.Empty : criteriaArray; + var returnKeyword = isDelete && !extension.IsAsync ? string.Empty : "return "; + + writer.WriteLine("/// "); + writer.WriteLine($"/// Invokes the {Escape(method.MethodDisplay)} {method.Kind} operation of ."); + writer.WriteLine("/// "); + writer.WriteLine($"{visibility} static {returnType} {extension.Name}({string.Join(", ", parameters)})"); + OpenBlock(writer); + writer.WriteLine($"if ({receiver} is {invokerType} __invoker)"); + writer.Indent++; + if (isDelete && !extension.IsAsync) + { + writer.WriteLine($"__invoker.{invokerMethod}({invokerArguments});"); + writer.Indent--; + writer.WriteLine("else"); + writer.Indent++; + writer.WriteLine($"{receiver}.{portalMethod}({fallbackArguments});"); + writer.Indent--; + } + else + { + writer.WriteLine($"{returnKeyword}__invoker.{invokerMethod}({invokerArguments});"); + writer.Indent--; + writer.WriteLine($"{returnKeyword}{receiver}.{portalMethod}({fallbackArguments});"); + } + CloseBlock(writer); + } + + private static string Escape(string text) => text.Replace("&", "&").Replace("<", "<").Replace(">", ">"); + + private static DiagnosticInfo Diagnostic(string id, LocationInfo? location, params string[] args) + => new(id, location, new EquatableArray(args)); + + private static void OpenBlock(IndentedTextWriter writer) + { + writer.WriteLine("{"); + writer.Indent++; + } + + private static void CloseBlock(IndentedTextWriter writer) + { + writer.Indent--; + writer.WriteLine("}"); + } + + private sealed record ExtensionMethod(OperationMethodModel Method, string Name, bool IsAsync, bool IsChild); + } +} diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/ExtensionTypeModel.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/ExtensionTypeModel.cs new file mode 100644 index 0000000000..265e2df6fa --- /dev/null +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/ExtensionTypeModel.cs @@ -0,0 +1,61 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// A business type marked with DataPortalExtensions +//----------------------------------------------------------------------- +// Portions adapted from Csla.DataPortalExtensions by Stefan Ossendorf +// (https://github.com/StefanOssendorf/Csla.DataPortalExtensions), +// licensed under the MIT License. Copyright (c) 2023 Stefan Ossendorf. +//----------------------------------------------------------------------- + +using Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations.Models; + +namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalExtensions +{ + /// + /// A business type marked with [DataPortalExtensions]. + /// + internal sealed record ExtensionTypeModel + { + /// Fully qualified metadata name, matching the operations model. + public required string MetadataName { get; init; } + + public required string Namespace { get; init; } + + /// Fully qualified business type name, e.g. "global::NS.PersonEdit". + public required string FullyQualifiedName { get; init; } + + /// Name of the generated static class, e.g. "PersonEditDataPortalExtensions". + public required string ExtensionClassName { get; init; } + + public required string TypeName { get; init; } + + /// File name friendly unique name. + public required string HintName { get; init; } + + public string Prefix { get; init; } = string.Empty; + + public bool IsGeneric { get; init; } + + public bool IsAbstract { get; init; } + + public bool IsCslaObject { get; init; } + + public TypeVisibility Visibility { get; init; } + + public LocationInfo? Location { get; init; } + + /// + /// Names of instance members of IDataPortal<T> and IChildDataPortal<T>, + /// which would hide extension methods with the same name. + /// + public required EquatableArray HiddenNames { get; init; } + } + + /// + /// A business type marked with [DataPortalExtensions] and its operation methods. + /// + internal sealed record ExtensionGenerationModel(ExtensionTypeModel Type, EquatableArray Methods); +} diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/IncrementalDataPortalExtensionsGenerator.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/IncrementalDataPortalExtensionsGenerator.cs new file mode 100644 index 0000000000..9432045340 --- /dev/null +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/IncrementalDataPortalExtensionsGenerator.cs @@ -0,0 +1,125 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// Source generator for strongly typed data portal extension methods +//----------------------------------------------------------------------- +// Portions adapted from Csla.DataPortalExtensions by Stefan Ossendorf +// (https://github.com/StefanOssendorf/Csla.DataPortalExtensions), +// licensed under the MIT License. Copyright (c) 2023 Stefan Ossendorf. +//----------------------------------------------------------------------- + +using System.Text; +using Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations; +using Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations.Models; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; + +namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalExtensions +{ + /// + /// Incremental source generator that emits strongly typed extension methods + /// on IDataPortal<T> and IChildDataPortal<T> for each + /// data portal operation method of a business class marked with + /// [DataPortalExtensions]. + /// + [Generator(LanguageNames.CSharp)] + public class IncrementalDataPortalExtensionsGenerator : IIncrementalGenerator + { + private const string DataPortalExtensionsAttributeName = "Csla.DataPortalExtensionsAttribute"; + + /// + /// Operation kinds that have a client-side data portal method. + /// + internal static readonly string[] SupportedKinds = ["Create", "Fetch", "Execute", "Delete", "CreateChild", "FetchChild"]; + + /// + public void Initialize(IncrementalGeneratorInitializationContext context) + { + var extensionTypes = context.SyntaxProvider.ForAttributeWithMetadataName( + DataPortalExtensionsAttributeName, + predicate: static (node, _) => node is ClassDeclarationSyntax, + transform: static (ctx, ct) => ExtractExtensionType(ctx, ct)) + .Where(static t => t is not null) + .Select(static (t, _) => t!) + .WithTrackingName(TrackingNames.ExtractExtensionTypes); + + var operationTypes = IncrementalDataPortalOperationsGenerator.GetOperationTypes(context); + + var models = extensionTypes + .Combine(operationTypes) + .Select(static (pair, _) => new ExtensionGenerationModel( + pair.Left, + new EquatableArray( + pair.Right + .Where(t => t.Type.MetadataName == pair.Left.MetadataName) + .SelectMany(t => t.Methods) + .Where(m => SupportedKinds.Contains(m.Kind))))) + .WithTrackingName(TrackingNames.ExtensionTypes); + + context.RegisterSourceOutput(models, static (spc, model) => + { + var (source, diagnostics) = DataPortalExtensionsBuilder.Build(model); + foreach (var diagnostic in diagnostics) + spc.ReportDiagnostic(diagnostic.ToDiagnostic()); + if (source is not null) + spc.AddSource($"{model.Type.HintName}.DataPortalExtensions.g.cs", SourceText.From(source, Encoding.UTF8)); + }); + } + + private static ExtensionTypeModel? ExtractExtensionType(GeneratorAttributeSyntaxContext context, CancellationToken ct) + { + if (context.TargetSymbol is not INamedTypeSymbol type) + return null; + + ct.ThrowIfCancellationRequested(); + + var attribute = context.Attributes.FirstOrDefault(); + var prefix = attribute?.NamedArguments + .Where(a => a.Key == "Prefix") + .Select(a => a.Value.Value as string) + .FirstOrDefault() ?? string.Empty; + + var containers = new List(); + for (var container = type.ContainingType; container is not null; container = container.ContainingType) + containers.Insert(0, container); + + var @namespace = type.ContainingNamespace is { IsGlobalNamespace: false } ns ? ns.ToDisplayString() : string.Empty; + var hintParts = new List(); + if (@namespace.Length > 0) + hintParts.Add(@namespace); + hintParts.AddRange(containers.Select(c => c.Name)); + hintParts.Add(type.Name); + + var compilation = context.SemanticModel.Compilation; + var hiddenNames = new SortedSet(StringComparer.Ordinal); + foreach (var portalInterface in new[] { "Csla.IDataPortal`1", "Csla.IChildDataPortal`1" }) + { + var symbol = compilation.GetTypeByMetadataName(portalInterface); + if (symbol is null) + continue; + foreach (var member in symbol.GetMembers()) + hiddenNames.Add(member.Name); + } + + return new ExtensionTypeModel + { + MetadataName = OperationDiscovery.GetFullMetadataName(type), + Namespace = @namespace, + FullyQualifiedName = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), + ExtensionClassName = string.Concat(containers.Select(c => c.Name + "_")) + type.Name + "DataPortalExtensions", + TypeName = type.Name, + HintName = string.Join(".", hintParts), + Prefix = prefix, + IsGeneric = type.TypeParameters.Length > 0 || containers.Any(c => c.TypeParameters.Length > 0), + IsAbstract = type.IsAbstract, + IsCslaObject = type.AllInterfaces.Any(i => i.ToDisplayString() == "Csla.Core.ICslaObject"), + Visibility = OperationDiscovery.GetVisibility(type), + Location = LocationInfo.From(type.Locations.FirstOrDefault()), + HiddenNames = new EquatableArray(hiddenNames) + }; + } + } +} diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs index 2bc2d7f686..fb94c2f90f 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs @@ -23,6 +23,7 @@ internal static class DataPortalOperationsDiagnostics public const string InvalidExtensionsTargetId = "CSLADP004"; public const string ExtensionNameHiddenId = "CSLADP005"; public const string InaccessibleParameterTypeId = "CSLADP006"; + public const string DuplicateExtensionMethodId = "CSLADP007"; public static readonly DiagnosticDescriptor DuplicateOperationName = new( id: DuplicateOperationNameId, @@ -64,6 +65,14 @@ internal static class DataPortalOperationsDiagnostics defaultSeverity: DiagnosticSeverity.Error, isEnabledByDefault: true); + public static readonly DiagnosticDescriptor DuplicateExtensionMethod = new( + id: DuplicateExtensionMethodId, + title: "Generated data portal extension would be duplicated", + messageFormat: "The data portal extension method '{0}' for '{1}' is not generated for '{2}' because another operation method already produces an extension method with the same signature", + category: Category, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + public static DiagnosticDescriptor GetDescriptor(string id) => id switch { DuplicateOperationNameId => DuplicateOperationName, @@ -71,6 +80,7 @@ internal static class DataPortalOperationsDiagnostics InvalidExtensionsTargetId => InvalidExtensionsTarget, ExtensionNameHiddenId => ExtensionNameHidden, InaccessibleParameterTypeId => InaccessibleParameterType, + DuplicateExtensionMethodId => DuplicateExtensionMethod, _ => throw new ArgumentOutOfRangeException(nameof(id), id, null) }; } diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs index 8c9b592c92..23ef07560e 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs @@ -310,7 +310,7 @@ private static string GetPartialDeclaration(INamedTypeSymbol type) private static string GetHintSegment(INamedTypeSymbol type) => type.TypeParameters.Length > 0 ? $"{type.Name}_{type.TypeParameters.Length}" : type.Name; - private static string GetFullMetadataName(INamedTypeSymbol type) + internal static string GetFullMetadataName(INamedTypeSymbol type) { var name = type.MetadataName; for (var container = type.ContainingType; container is not null; container = container.ContainingType) diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/readme.md b/Source/Csla.Generators/cs/AutoImplementProperties/readme.md index 29a0e176de..e168437268 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/readme.md +++ b/Source/Csla.Generators/cs/AutoImplementProperties/readme.md @@ -5,8 +5,7 @@ This guide provides a step-by-step approach to using the source generator in the ## Prerequisites - Visual Studio 2019 or later with the .NET Core development workload installed. -- The CSLA.NET NuGet package added to your project. -- Ths CSLA.NET Source Generator NuGet package added to your project. +- The CSLA.NET NuGet package added to your project. Starting with CSLA 11 the source generators are included in the `Csla` package; the separate `Csla.Generator.AutoImplementProperties.CSharp` package is no longer used. ## Step 1: Define Your POCO Class diff --git a/Source/Csla/DataPortalT.cs b/Source/Csla/DataPortalT.cs index 59cdb9adfb..a685169993 100644 --- a/Source/Csla/DataPortalT.cs +++ b/Source/Csla/DataPortalT.cs @@ -25,7 +25,8 @@ namespace Csla /// Type of business object. /// public class DataPortal<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T> - : IDataPortal, IChildDataPortal, IDataPortal, IChildDataPortal where T : notnull, ICslaObject + : IDataPortal, IChildDataPortal, IDataPortal, IChildDataPortal, + IDataPortalOperationInvoker, IChildDataPortalOperationInvoker where T : notnull, ICslaObject { /// /// Gets or sets the current ApplicationContext object. @@ -66,16 +67,21 @@ private Reflection.ServiceProviderMethodCaller ServiceProviderMethodCaller private async Task DoCreateAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, object criteria, bool isSync, CancellationToken ct = default) { + _ = ServiceProviderMethodCaller.TryGetProviderMethodInfoFor(objectType, criteria, out var method); + var operationName = method != null ? Server.DataPortalOperationNameHelper.ComputeOperationName(method.MethodInfo) : null; + return await DoCreateCoreAsync(objectType, criteria, isSync, operationName, method?.MethodInfo.RunLocal() ?? false, ct); + } + private async Task DoCreateCoreAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, object criteria, bool isSync, string? operationName, bool runLocal, CancellationToken ct = default) + { if (!await Rules.BusinessRules.HasPermissionAsync(_applicationContext, Rules.AuthorizationActions.CreateObject, objectType, Server.DataPortal.GetCriteriaArray(criteria), ct)) throw new Security.SecurityException(string.Format(Resources.UserNotAuthorizedException, "create", objectType.Name)); - _ = ServiceProviderMethodCaller.TryGetProviderMethodInfoFor(objectType, criteria, out var method); - var proxy = GetDataPortalProxy(method); - - var dpContext = new Server.DataPortalContext(_applicationContext, proxy.IsServerRemote); - if (method != null) - dpContext.OperationName = Server.DataPortalOperationNameHelper.ComputeOperationName(method.MethodInfo); + var proxy = GetDataPortalProxy(runLocal); + var dpContext = new Server.DataPortalContext(_applicationContext, proxy.IsServerRemote) + { + OperationName = operationName + }; Server.DataPortalResult result = default!; try @@ -146,15 +152,21 @@ private async Task DoFetchAsync([DynamicallyAccessedMembers(DynamicallyA return await DoExecuteAsync(objectType, criteria, isSync); } + _ = ServiceProviderMethodCaller.TryGetProviderMethodInfoFor(objectType, criteria, out var method); + var operationName = method != null ? Server.DataPortalOperationNameHelper.ComputeOperationName(method.MethodInfo) : null; + return await DoFetchCoreAsync(objectType, criteria, isSync, operationName, method?.MethodInfo.RunLocal() ?? false, ct); + } + + private async Task DoFetchCoreAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, object criteria, bool isSync, string? operationName, bool runLocal, CancellationToken ct = default) + { if (!await Rules.BusinessRules.HasPermissionAsync(_applicationContext, Rules.AuthorizationActions.GetObject, objectType, Server.DataPortal.GetCriteriaArray(criteria), ct)) throw new Security.SecurityException(string.Format(Resources.UserNotAuthorizedException, "get", objectType.Name)); - _ = ServiceProviderMethodCaller.TryGetProviderMethodInfoFor(objectType, criteria, out var method); - - var proxy = GetDataPortalProxy(method); - var dpContext = new Server.DataPortalContext(_applicationContext, proxy.IsServerRemote); - if (method != null) - dpContext.OperationName = Server.DataPortalOperationNameHelper.ComputeOperationName(method.MethodInfo); + var proxy = GetDataPortalProxy(runLocal); + var dpContext = new Server.DataPortalContext(_applicationContext, proxy.IsServerRemote) + { + OperationName = operationName + }; Server.DataPortalResult result = default!; try @@ -181,17 +193,23 @@ private async Task DoFetchAsync([DynamicallyAccessedMembers(DynamicallyA private async Task DoExecuteAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, object criteria, bool isSync, CancellationToken ct = default) { + _ = ServiceProviderMethodCaller.TryGetProviderMethodInfoFor(objectType, criteria, out var method); + var operationName = method != null ? Server.DataPortalOperationNameHelper.ComputeOperationName(method.MethodInfo) : null; + return await DoExecuteCoreAsync(objectType, criteria, isSync, operationName, method?.MethodInfo.RunLocal() ?? false, ct); + } + private async Task DoExecuteCoreAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, object criteria, bool isSync, string? operationName, bool runLocal, CancellationToken ct = default) + { if (!await Rules.BusinessRules.HasPermissionAsync(_applicationContext, Rules.AuthorizationActions.EditObject, objectType, Server.DataPortal.GetCriteriaArray(criteria), ct)) throw new Security.SecurityException(string.Format(Resources.UserNotAuthorizedException, "execute", objectType.Name)); - _ = ServiceProviderMethodCaller.TryGetProviderMethodInfoFor(objectType, criteria, out var method); - var proxy = GetDataPortalProxy(method); - var dpContext = new Server.DataPortalContext(_applicationContext, proxy.IsServerRemote); - if (method != null) - dpContext.OperationName = Server.DataPortalOperationNameHelper.ComputeOperationName(method.MethodInfo); + var proxy = GetDataPortalProxy(runLocal); + var dpContext = new Server.DataPortalContext(_applicationContext, proxy.IsServerRemote) + { + OperationName = operationName + }; Server.DataPortalResult result = default!; try @@ -469,16 +487,21 @@ public Task UpdateAsync(T obj) private async Task DoDeleteAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, object criteria, bool isSync, CancellationToken ct = default) { + _ = ServiceProviderMethodCaller.TryGetProviderMethodInfoFor(objectType, criteria, out var method); + var operationName = method != null ? Server.DataPortalOperationNameHelper.ComputeOperationName(method.MethodInfo) : null; + await DoDeleteCoreAsync(objectType, criteria, isSync, operationName, method?.MethodInfo.RunLocal() ?? false, ct); + } + private async Task DoDeleteCoreAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, object criteria, bool isSync, string? operationName, bool runLocal, CancellationToken ct = default) + { if (!await Rules.BusinessRules.HasPermissionAsync(_applicationContext, Rules.AuthorizationActions.DeleteObject, objectType, Server.DataPortal.GetCriteriaArray(criteria), ct)) throw new Security.SecurityException(string.Format(Resources.UserNotAuthorizedException, "delete", objectType.Name)); - _ = ServiceProviderMethodCaller.TryGetProviderMethodInfoFor(objectType, criteria, out var method); - var proxy = GetDataPortalProxy(method); - - var dpContext = new Server.DataPortalContext(_applicationContext, proxy.IsServerRemote); - if (method != null) - dpContext.OperationName = Server.DataPortalOperationNameHelper.ComputeOperationName(method.MethodInfo); + var proxy = GetDataPortalProxy(runLocal); + var dpContext = new Server.DataPortalContext(_applicationContext, proxy.IsServerRemote) + { + OperationName = operationName + }; try { var result = await _cache.GetDataPortalResultAsync(objectType, criteria, DataPortalOperations.Delete, @@ -572,6 +595,113 @@ public async Task ExecuteAsync(params object?[]? criteria) return (T)await DoFetchAsync(typeof(T), Server.DataPortal.GetCriteriaFromArray(criteria), false); } + #region Pre-resolved operations + + /// + /// Converts the explicit criteria array supplied by generated code into + /// the criteria object used by the data portal. A single criteria value + /// that is itself a covariant array (for example string[]) is wrapped so + /// it is not expanded into separate criteria values. + /// + private static object GetOperationCriteria(object?[]? criteria) + { + if (criteria is { Length: 1 } && criteria[0] is object[] array && array.GetType() != typeof(object[])) + return new Core.MobileList(criteria); + return Server.DataPortal.GetCriteriaFromArray(criteria); + } + + private static void WaitSynchronously(Task task) + { + try + { + task.Wait(); + } + catch (AggregateException ex) + { + if (ex.InnerExceptions.Count > 0) + throw ex.InnerExceptions[0]; + else + throw; + } + } + + private static TResult WaitSynchronously(Task task) + { + WaitSynchronously((Task)task); + return task.Result; + } + + /// + T IDataPortalOperationInvoker.CreateByOperation(string operationName, bool runLocal, object?[]? criteria) + => (T)WaitSynchronously(DoCreateCoreAsync(typeof(T), GetOperationCriteria(criteria), true, operationName, runLocal)); + + /// + async Task IDataPortalOperationInvoker.CreateByOperationAsync(string operationName, bool runLocal, object?[]? criteria) + => (T)await DoCreateCoreAsync(typeof(T), GetOperationCriteria(criteria), false, operationName, runLocal); + + /// + T IDataPortalOperationInvoker.FetchByOperation(string operationName, bool runLocal, object?[]? criteria) + => (T)WaitSynchronously(DoFetchByOperationAsync(operationName, runLocal, criteria, true)); + + /// + async Task IDataPortalOperationInvoker.FetchByOperationAsync(string operationName, bool runLocal, object?[]? criteria) + => (T)await DoFetchByOperationAsync(operationName, runLocal, criteria, false); + + private Task DoFetchByOperationAsync(string operationName, bool runLocal, object?[]? criteria, bool isSync) + { + // As with FetchAsync, fetching a command object is an execute + // operation; the fetch operation name does not apply to it. + if (typeof(ICommandObject).IsAssignableFrom(typeof(T))) + return DoExecuteCoreAsync(typeof(T), GetOperationCriteria(criteria), isSync, null, runLocal); + return DoFetchCoreAsync(typeof(T), GetOperationCriteria(criteria), isSync, operationName, runLocal); + } + + /// + T IDataPortalOperationInvoker.ExecuteByOperation(string operationName, bool runLocal, object?[]? criteria) + => (T)WaitSynchronously(DoExecuteCoreAsync(typeof(T), GetOperationCriteria(criteria), true, operationName, runLocal)); + + /// + async Task IDataPortalOperationInvoker.ExecuteByOperationAsync(string operationName, bool runLocal, object?[]? criteria) + => (T)await DoExecuteCoreAsync(typeof(T), GetOperationCriteria(criteria), false, operationName, runLocal); + + /// + void IDataPortalOperationInvoker.DeleteByOperation(string operationName, bool runLocal, object?[]? criteria) + => WaitSynchronously(DoDeleteCoreAsync(typeof(T), GetOperationCriteria(criteria), true, operationName, runLocal)); + + /// + Task IDataPortalOperationInvoker.DeleteByOperationAsync(string operationName, bool runLocal, object?[]? criteria) + => DoDeleteCoreAsync(typeof(T), GetOperationCriteria(criteria), false, operationName, runLocal); + + /// + T IChildDataPortalOperationInvoker.CreateChildByOperation(string operationName, object?[]? criteria) + { + var portal = new Server.ChildDataPortal(_applicationContext); + return (T)portal.CreateByOperation(typeof(T), operationName, criteria); + } + + /// + Task IChildDataPortalOperationInvoker.CreateChildByOperationAsync(string operationName, object?[]? criteria) + { + var portal = new Server.ChildDataPortal(_applicationContext); + return portal.CreateByOperationAsync(operationName, criteria); + } + + /// + T IChildDataPortalOperationInvoker.FetchChildByOperation(string operationName, object?[]? criteria) + { + var portal = new Server.ChildDataPortal(_applicationContext); + return (T)portal.FetchByOperation(typeof(T), operationName, criteria); + } + + /// + Task IChildDataPortalOperationInvoker.FetchChildByOperationAsync(string operationName, object?[]? criteria) + { + var portal = new Server.ChildDataPortal(_applicationContext); + return portal.FetchByOperationAsync(operationName, criteria); + } + + #endregion + private IDataPortalProxy GetDataPortalProxy(Reflection.ServiceProviderMethodInfo? method) { if (method != null) diff --git a/Source/Csla/IDataPortalOperationInvoker.cs b/Source/Csla/IDataPortalOperationInvoker.cs new file mode 100644 index 0000000000..1d941266de --- /dev/null +++ b/Source/Csla/IDataPortalOperationInvoker.cs @@ -0,0 +1,134 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// Invokes pre-resolved data portal operations +//----------------------------------------------------------------------- + +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using Csla.Core; + +namespace Csla +{ + /// + /// Invokes data portal operations whose target method has already been + /// resolved at compile time. Used by generated data portal extension + /// methods; not intended to be called directly. + /// + /// + /// The operation name identifies the operation method on the server + /// and the runLocal flag reflects any + /// on that method, so the client does + /// not need to locate the method using reflection. + /// + /// Type of business object. + [EditorBrowsable(EditorBrowsableState.Never)] + public interface IDataPortalOperationInvoker<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T> + where T : ICslaObject + { + /// + /// Creates a business object using a pre-resolved create operation. + /// + /// Generated operation name. + /// True to run the operation on the client. + /// Criteria values, one element per criteria parameter. + T CreateByOperation(string operationName, bool runLocal, object?[]? criteria); + + /// + /// Creates a business object using a pre-resolved create operation. + /// + /// Generated operation name. + /// True to run the operation on the client. + /// Criteria values, one element per criteria parameter. + Task CreateByOperationAsync(string operationName, bool runLocal, object?[]? criteria); + + /// + /// Fetches a business object using a pre-resolved fetch operation. + /// + /// Generated operation name. + /// True to run the operation on the client. + /// Criteria values, one element per criteria parameter. + T FetchByOperation(string operationName, bool runLocal, object?[]? criteria); + + /// + /// Fetches a business object using a pre-resolved fetch operation. + /// + /// Generated operation name. + /// True to run the operation on the client. + /// Criteria values, one element per criteria parameter. + Task FetchByOperationAsync(string operationName, bool runLocal, object?[]? criteria); + + /// + /// Executes a command object using a pre-resolved execute operation. + /// + /// Generated operation name. + /// True to run the operation on the client. + /// Criteria values, one element per criteria parameter. + T ExecuteByOperation(string operationName, bool runLocal, object?[]? criteria); + + /// + /// Executes a command object using a pre-resolved execute operation. + /// + /// Generated operation name. + /// True to run the operation on the client. + /// Criteria values, one element per criteria parameter. + Task ExecuteByOperationAsync(string operationName, bool runLocal, object?[]? criteria); + + /// + /// Deletes a business object using a pre-resolved delete operation. + /// + /// Generated operation name. + /// True to run the operation on the client. + /// Criteria values, one element per criteria parameter. + void DeleteByOperation(string operationName, bool runLocal, object?[]? criteria); + + /// + /// Deletes a business object using a pre-resolved delete operation. + /// + /// Generated operation name. + /// True to run the operation on the client. + /// Criteria values, one element per criteria parameter. + Task DeleteByOperationAsync(string operationName, bool runLocal, object?[]? criteria); + } + + /// + /// Invokes child data portal operations whose target method has already + /// been resolved at compile time. Used by generated data portal extension + /// methods; not intended to be called directly. + /// + /// Type of business object. + [EditorBrowsable(EditorBrowsableState.Never)] + public interface IChildDataPortalOperationInvoker<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T> + where T : ICslaObject + { + /// + /// Creates a child business object using a pre-resolved create child operation. + /// + /// Generated operation name. + /// Criteria values, one element per criteria parameter. + T CreateChildByOperation(string operationName, object?[]? criteria); + + /// + /// Creates a child business object using a pre-resolved create child operation. + /// + /// Generated operation name. + /// Criteria values, one element per criteria parameter. + Task CreateChildByOperationAsync(string operationName, object?[]? criteria); + + /// + /// Fetches a child business object using a pre-resolved fetch child operation. + /// + /// Generated operation name. + /// Criteria values, one element per criteria parameter. + T FetchChildByOperation(string operationName, object?[]? criteria); + + /// + /// Fetches a child business object using a pre-resolved fetch child operation. + /// + /// Generated operation name. + /// Criteria values, one element per criteria parameter. + Task FetchChildByOperationAsync(string operationName, object?[]? criteria); + } +} diff --git a/Source/Csla/Server/ChildDataPortal.cs b/Source/Csla/Server/ChildDataPortal.cs index ecfc6ca079..26fb860f68 100644 --- a/Source/Csla/Server/ChildDataPortal.cs +++ b/Source/Csla/Server/ChildDataPortal.cs @@ -6,6 +6,7 @@ // Invoke data portal methods on child //----------------------------------------------------------------------- +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using Csla.Core; @@ -43,7 +44,7 @@ public object Create([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes. throw new ArgumentNullException(nameof(objectType)); return ExecuteWithAggregateExceptionHandling( - () => DoCreateAsync(objectType).Result + () => DoCreateAsync(objectType, null).Result ); } @@ -61,7 +62,7 @@ public object Create([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes. throw new ArgumentNullException(nameof(objectType)); return ExecuteWithAggregateExceptionHandling( - () => DoCreateAsync(objectType, parameters).Result + () => DoCreateAsync(objectType, null, parameters).Result ); } @@ -70,7 +71,7 @@ public object Create([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes. /// public async Task CreateAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>() { - return (T) await DoCreateAsync(typeof(T)).ConfigureAwait(false); + return (T)await DoCreateAsync(typeof(T), null).ConfigureAwait(false); } /// @@ -81,10 +82,45 @@ public object Create([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes. /// public async Task CreateAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(params object?[]? parameters) { - return (T)await DoCreateAsync(typeof(T), parameters).ConfigureAwait(false); + return (T)await DoCreateAsync(typeof(T), null, parameters).ConfigureAwait(false); } - private async Task DoCreateAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, params object?[]? parameters) + /// + /// Create a new business object using a pre-resolved create child operation. + /// + /// Type of business object to create. + /// Generated operation name. + /// Criteria values, one element per criteria parameter. + /// or is . + [EditorBrowsable(EditorBrowsableState.Never)] + public object CreateByOperation([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, string operationName, object?[]? parameters) + { + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (operationName is null) + throw new ArgumentNullException(nameof(operationName)); + + return ExecuteWithAggregateExceptionHandling( + () => DoCreateAsync(objectType, operationName, parameters).Result + ); + } + + /// + /// Create a new business object using a pre-resolved create child operation. + /// + /// Generated operation name. + /// Criteria values, one element per criteria parameter. + /// is . + [EditorBrowsable(EditorBrowsableState.Never)] + public async Task CreateByOperationAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(string operationName, object?[]? parameters) + { + if (operationName is null) + throw new ArgumentNullException(nameof(operationName)); + + return (T)await DoCreateAsync(typeof(T), operationName, parameters).ConfigureAwait(false); + } + + private async Task DoCreateAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, string? operationName, params object?[]? parameters) { DataPortalTarget? obj = null; var eventArgs = new DataPortalEventArgs(null, objectType, parameters, DataPortalOperations.Create); @@ -95,7 +131,7 @@ private async Task DoCreateAsync([DynamicallyAccessedMembers(Dynamically obj.Child_OnDataPortalInvoke(eventArgs); obj.MarkAsChild(); obj.MarkNew(); - await obj.CreateChildAsync(parameters).ConfigureAwait(false); + await obj.CreateChildAsync(operationName, parameters).ConfigureAwait(false); obj.Child_OnDataPortalInvokeComplete(eventArgs); return obj.Instance; @@ -134,7 +170,7 @@ public object Fetch([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.P throw new ArgumentNullException(nameof(objectType)); return ExecuteWithAggregateExceptionHandling( - () => DoFetchAsync(objectType).Result + () => DoFetchAsync(objectType, null).Result ); } @@ -152,7 +188,7 @@ public object Fetch([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.P throw new ArgumentNullException(nameof(objectType)); return ExecuteWithAggregateExceptionHandling( - () => DoFetchAsync(objectType, parameters).Result + () => DoFetchAsync(objectType, null, parameters).Result ); } @@ -161,7 +197,7 @@ public object Fetch([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.P /// public async Task FetchAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>() { - return (T)await DoFetchAsync(typeof(T)).ConfigureAwait(false); + return (T)await DoFetchAsync(typeof(T), null).ConfigureAwait(false); } /// @@ -172,10 +208,45 @@ public object Fetch([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.P /// public async Task FetchAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(params object?[]? parameters) { - return (T)await DoFetchAsync(typeof(T), parameters).ConfigureAwait(false); + return (T)await DoFetchAsync(typeof(T), null, parameters).ConfigureAwait(false); + } + + /// + /// Get an existing business object using a pre-resolved fetch child operation. + /// + /// Type of business object to retrieve. + /// Generated operation name. + /// Criteria values, one element per criteria parameter. + /// or is . + [EditorBrowsable(EditorBrowsableState.Never)] + public object FetchByOperation([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, string operationName, object?[]? parameters) + { + if (objectType is null) + throw new ArgumentNullException(nameof(objectType)); + if (operationName is null) + throw new ArgumentNullException(nameof(operationName)); + + return ExecuteWithAggregateExceptionHandling( + () => DoFetchAsync(objectType, operationName, parameters).Result + ); + } + + /// + /// Get an existing business object using a pre-resolved fetch child operation. + /// + /// Generated operation name. + /// Criteria values, one element per criteria parameter. + /// is . + [EditorBrowsable(EditorBrowsableState.Never)] + public async Task FetchByOperationAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(string operationName, object?[]? parameters) + { + if (operationName is null) + throw new ArgumentNullException(nameof(operationName)); + + return (T)await DoFetchAsync(typeof(T), operationName, parameters).ConfigureAwait(false); } - private async Task DoFetchAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, params object?[]? parameters) + private async Task DoFetchAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type objectType, string? operationName, params object?[]? parameters) { DataPortalTarget? obj = null; var eventArgs = new DataPortalEventArgs(null, objectType, parameters, DataPortalOperations.Fetch); @@ -188,7 +259,7 @@ private async Task DoFetchAsync([DynamicallyAccessedMembers(DynamicallyA obj.Child_OnDataPortalInvoke(eventArgs); obj.MarkAsChild(); obj.MarkOld(); - await obj.FetchChildAsync(parameters).ConfigureAwait(false); + await obj.FetchChildAsync(operationName, parameters).ConfigureAwait(false); obj.Child_OnDataPortalInvokeComplete(eventArgs); return obj.Instance; } diff --git a/Source/Csla/Server/DataPortalTarget.cs b/Source/Csla/Server/DataPortalTarget.cs index c934ca1cae..d8e057a078 100644 --- a/Source/Csla/Server/DataPortalTarget.cs +++ b/Source/Csla/Server/DataPortalTarget.cs @@ -200,9 +200,26 @@ await _operationMapping.InvokeOperationAsync( await CallMethodTryAsyncDI(isSync, parameters).ConfigureAwait(false); } - private async Task InvokeChildOperationAsync(object?[]? parameters) + private async Task InvokeChildOperationAsync(object?[]? parameters, string? operationName = null) where T : DataPortalChildOperationAttribute { + // Try name-based dispatch first + if (operationName != null && _namedMapping != null) + { + try + { + var serviceProvider = _applicationContext.CurrentServiceProvider; + await _namedMapping.InvokeNamedOperationAsync( + operationName, false, parameters, serviceProvider + ).ConfigureAwait(false); + return; + } + catch (DataPortalOperationNotSupportedException) + { + // Fall through to criteria-based path + } + } + if (_operationMapping != null) { try @@ -231,6 +248,11 @@ public Task CreateChildAsync(params object?[]? parameters) return InvokeChildOperationAsync(parameters); } + public Task CreateChildAsync(string? operationName, object?[]? parameters) + { + return InvokeChildOperationAsync(parameters, operationName); + } + public Task FetchAsync(object criteria, bool isSync, string? operationName = null) { return InvokeOperationAsync(criteria, isSync, operationName); @@ -241,6 +263,11 @@ public Task FetchChildAsync(params object?[]? parameters) return InvokeChildOperationAsync(parameters); } + public Task FetchChildAsync(string? operationName, object?[]? parameters) + { + return InvokeChildOperationAsync(parameters, operationName); + } + public Task ExecuteAsync(object criteria, bool isSync, string? operationName = null) { return InvokeOperationAsync(criteria, isSync, operationName); diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/DataPortalExtensionsGeneratorTests.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/DataPortalExtensionsGeneratorTests.cs new file mode 100644 index 0000000000..ee15d07407 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/DataPortalExtensionsGeneratorTests.cs @@ -0,0 +1,381 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +//----------------------------------------------------------------------- +using Csla.Generator.AutoImplementProperties.CSharp.DataPortalExtensions; +using Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations; +using Csla.Generator.AutoImplementProperties.CSharp.Tests.DataPortalOperations; + +namespace Csla.Generator.AutoImplementProperties.CSharp.Tests.DataPortalExtensions +{ + [TestClass] + public class DataPortalExtensionsGeneratorTests : VerifyBase + { + [TestMethod("Async and sync extensions for root operations")] + public async Task RootOperations() + { + var source = """ + using Csla; + using System.Threading.Tasks; + + namespace TestApp + { + public interface IDal { } + + [DataPortalExtensions] + public partial class PersonEdit : BusinessBase + { + [Create] + private void New() { } + + [Fetch] + private async Task GetById(int id, [Inject] IDal dal) { await Task.CompletedTask; } + + [Delete] + private Task RemoveAsync(int id) => Task.CompletedTask; + + [RunLocal] + [Fetch] + private void GetLocal([Inject] IDal dal, string code) { } + } + } + """; + + await Verify(source); + } + + [TestMethod("Extensions for child operations")] + public async Task ChildOperations() + { + var source = """ + using Csla; + + namespace TestApp + { + [DataPortalExtensions] + public partial class LineItem : BusinessBase + { + [CreateChild] + private void NewChild() { } + + [FetchChild] + private void LoadChild(int id, string name) { } + } + } + """; + + await Verify(source); + } + + [TestMethod("Execute operation on a command object")] + public async Task ExecuteCommand() + { + var source = """ + using Csla; + + namespace TestApp + { + [DataPortalExtensions] + public class RecalculateCommand : CommandBase + { + [Execute] + private void Run(int count) { } + } + } + """; + + await Verify(source); + } + + [TestMethod("Prefix gives extensions distinct names")] + public async Task Prefix() + { + var source = """ + using Csla; + + namespace TestApp + { + [DataPortalExtensions(Prefix = "Portal")] + public partial class PersonEdit : BusinessBase + { + [Create] + private void Create() { } + + [Fetch] + private void Fetch(int id) { } + } + } + """; + + await Verify(source); + } + + [TestMethod("Names that would be hidden by data portal instance methods are reported")] + public async Task HiddenNames() + { + var source = """ + using Csla; + using System.Threading.Tasks; + + namespace TestApp + { + [DataPortalExtensions] + public partial class PersonEdit : BusinessBase + { + [Fetch] + private void Fetch(int id) { } + + [Fetch] + private Task FetchAsync(string name) => Task.CompletedTask; + + [Fetch] + private void GetByCode(string code, int version) { } + } + } + """; + + await VerifyWithDiagnostics(source); + } + + [TestMethod("NoDataPortalExtension excludes a method")] + public async Task NoDataPortalExtension() + { + var source = """ + using Csla; + + namespace TestApp + { + [DataPortalExtensions] + public partial class PersonEdit : BusinessBase + { + [Fetch] + private void GetById(int id) { } + + [NoDataPortalExtension] + [Fetch] + private void GetByName(string name) { } + } + } + """; + + await Verify(source); + } + + [TestMethod("Internal parameter types make the extension internal and private types are reported")] + public async Task ParameterAccessibility() + { + var source = """ + using Csla; + + namespace TestApp + { + internal class InternalCriteria { } + + [DataPortalExtensions] + public partial class PersonEdit : BusinessBase + { + private class PrivateCriteria { } + + [Fetch] + private void GetByInternal(InternalCriteria criteria) { } + + [Fetch] + private void GetByPrivate(PrivateCriteria criteria) { } + } + } + """; + + await VerifyWithDiagnostics(source); + } + + [TestMethod("Default parameter values including enums")] + public async Task DefaultValues() + { + var source = """ + using Csla; + + namespace TestApp + { + public enum Color { Red, Green = 5 } + + [DataPortalExtensions] + public partial class PersonEdit : BusinessBase + { + [Fetch] + private void GetById(int id = 5, string? name = "x\"y", Color color = Color.Green, decimal amount = 1.5m, double? ratio = null, float scale = 2.25f, long big = -3, bool active = true, char marker = 'a') { } + } + } + """; + + await Verify(source); + } + + [TestMethod("Array and params criteria")] + public async Task ArrayCriteria() + { + var source = """ + using Csla; + + namespace TestApp + { + [DataPortalExtensions] + public partial class PersonEdit : BusinessBase + { + [Fetch] + private void GetByNames(string[] names) { } + + [Create] + private void NewWith(params int[] values) { } + } + } + """; + + await Verify(source); + } + + [TestMethod("Nested business type")] + public async Task NestedType() + { + var source = """ + using Csla; + + namespace TestApp + { + public partial class Outer + { + [DataPortalExtensions] + public partial class PersonEdit : BusinessBase + { + [Fetch] + private void GetById(int id) { } + } + } + } + """; + + await Verify(source); + } + + [TestMethod("Internal business type generates an internal extension class")] + public async Task InternalType() + { + var source = """ + using Csla; + + namespace TestApp + { + [DataPortalExtensions] + internal partial class PersonEdit : BusinessBase + { + [Fetch] + private void GetById(int id) { } + } + } + """; + + await Verify(source); + } + + [TestMethod("Generic business type is skipped")] + public async Task GenericTypeSkipped() + { + var source = """ + using Csla; + + namespace TestApp + { + [DataPortalExtensions] + public partial class Lookup : ReadOnlyBase> + { + [Fetch] + private void GetByKey(TKey key) { } + } + } + """; + + await VerifyWithDiagnostics(source); + } + + [TestMethod("Abstract and non-CSLA types are reported")] + public async Task InvalidTargets() + { + var source = """ + using Csla; + + namespace TestApp + { + [DataPortalExtensions] + public abstract partial class PersonBase : BusinessBase + { + [Fetch] + private void GetById(int id) { } + } + + [DataPortalExtensions] + public class NotABusinessObject + { + [Fetch] + private void GetById(int id) { } + } + } + """; + + await VerifyWithDiagnostics(source); + } + + [TestMethod("Operations producing the same extension signature are reported")] + public async Task DuplicateExtension() + { + var source = """ + using Csla; + + namespace TestApp + { + [DataPortalExtensions] + public partial class PersonEdit : BusinessBase + { + [Fetch] + private void Get(int id) { } + + [Create] + private void Get(int id, [Inject] System.IServiceProvider services) { } + } + } + """; + + await VerifyWithDiagnostics(source); + } + + [TestMethod("Pipeline stages are cached when the compilation does not change")] + public void StageCaching() + { + var source = """ + using Csla; + + namespace TestApp + { + [DataPortalExtensions(Prefix = "Portal")] + public partial class PersonEdit : BusinessBase + { + [Create] + private void Create() { } + + [FetchChild] + [Fetch] + private void Fetch(int id) { } + } + } + """; + + StageCachingTester.VerifyStageCaching(source, typeof(TrackingNames)); + } + + private static Task Verify(string source, [System.Runtime.CompilerServices.CallerFilePath] string sourceFile = "") + => DataPortalOperationsTestHelper.Verify(source, sourceFile: sourceFile); + + private static Task VerifyWithDiagnostics(string source, [System.Runtime.CompilerServices.CallerFilePath] string sourceFile = "") + => DataPortalOperationsTestHelper.VerifyWithDiagnostics(source, sourceFile: sourceFile); + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ArrayCriteria#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ArrayCriteria#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs new file mode 100644 index 0000000000..9767ff97ae --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ArrayCriteria#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs @@ -0,0 +1,52 @@ +//HintName: TestApp.PersonEdit.DataPortalExtensions.g.cs +// +#nullable enable + +namespace TestApp +{ + /// + /// Strongly typed data portal extension methods for . + /// + public static partial class PersonEditDataPortalExtensions + { + /// + /// Invokes the GetByNames(string[]) Fetch operation of . + /// + public static global::System.Threading.Tasks.Task GetByNamesAsync(this global::Csla.IDataPortal portal, string[] names) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperationAsync("Fetch__StringArray", false, new object?[] { names }); + return portal.FetchAsync(new object?[] { names }); + } + + /// + /// Invokes the GetByNames(string[]) Fetch operation of . + /// + public static global::TestApp.PersonEdit GetByNames(this global::Csla.IDataPortal portal, string[] names) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperation("Fetch__StringArray", false, new object?[] { names }); + return portal.Fetch(new object?[] { names }); + } + + /// + /// Invokes the NewWith(int[]) Create operation of . + /// + public static global::System.Threading.Tasks.Task NewWithAsync(this global::Csla.IDataPortal portal, params int[] values) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.CreateByOperationAsync("Create__Int32Array", false, new object?[] { values }); + return portal.CreateAsync(new object?[] { values }); + } + + /// + /// Invokes the NewWith(int[]) Create operation of . + /// + public static global::TestApp.PersonEdit NewWith(this global::Csla.IDataPortal portal, params int[] values) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.CreateByOperation("Create__Int32Array", false, new object?[] { values }); + return portal.Create(new object?[] { values }); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalExtensions.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalExtensions.g.verified.cs new file mode 100644 index 0000000000..d9e94d57dd --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalExtensions.g.verified.cs @@ -0,0 +1,52 @@ +//HintName: TestApp.LineItem.DataPortalExtensions.g.cs +// +#nullable enable + +namespace TestApp +{ + /// + /// Strongly typed data portal extension methods for . + /// + public static partial class LineItemDataPortalExtensions + { + /// + /// Invokes the NewChild() CreateChild operation of . + /// + public static global::System.Threading.Tasks.Task NewChildAsync(this global::Csla.IChildDataPortal portal) + { + if (portal is global::Csla.IChildDataPortalOperationInvoker __invoker) + return __invoker.CreateChildByOperationAsync("CreateChild", global::System.Array.Empty()); + return portal.CreateChildAsync(); + } + + /// + /// Invokes the NewChild() CreateChild operation of . + /// + public static global::TestApp.LineItem NewChild(this global::Csla.IChildDataPortal portal) + { + if (portal is global::Csla.IChildDataPortalOperationInvoker __invoker) + return __invoker.CreateChildByOperation("CreateChild", global::System.Array.Empty()); + return portal.CreateChild(); + } + + /// + /// Invokes the LoadChild(int, string) FetchChild operation of . + /// + public static global::System.Threading.Tasks.Task LoadChildAsync(this global::Csla.IChildDataPortal portal, int id, string name) + { + if (portal is global::Csla.IChildDataPortalOperationInvoker __invoker) + return __invoker.FetchChildByOperationAsync("FetchChild__Int32_String", new object?[] { id, name }); + return portal.FetchChildAsync(new object?[] { id, name }); + } + + /// + /// Invokes the LoadChild(int, string) FetchChild operation of . + /// + public static global::TestApp.LineItem LoadChild(this global::Csla.IChildDataPortal portal, int id, string name) + { + if (portal is global::Csla.IChildDataPortalOperationInvoker __invoker) + return __invoker.FetchChildByOperation("FetchChild__Int32_String", new object?[] { id, name }); + return portal.FetchChild(new object?[] { id, name }); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.DefaultValues#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.DefaultValues#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs new file mode 100644 index 0000000000..df34ccc885 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.DefaultValues#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs @@ -0,0 +1,32 @@ +//HintName: TestApp.PersonEdit.DataPortalExtensions.g.cs +// +#nullable enable + +namespace TestApp +{ + /// + /// Strongly typed data portal extension methods for . + /// + public static partial class PersonEditDataPortalExtensions + { + /// + /// Invokes the GetById(int, string, Color, decimal, double?, float, long, bool, char) Fetch operation of . + /// + public static global::System.Threading.Tasks.Task GetByIdAsync(this global::Csla.IDataPortal portal, int id = 5, string? name = "x\"y", global::TestApp.Color color = global::TestApp.Color.Green, decimal amount = 1.5M, double? ratio = null, float scale = 2.25F, long big = -3L, bool active = true, char marker = 'a') + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperationAsync("Fetch__Int32_String_Color_Decimal_Nullable_1_Double_Single_Int64_Boolean_Char", false, new object?[] { id, name, color, amount, ratio, scale, big, active, marker }); + return portal.FetchAsync(new object?[] { id, name, color, amount, ratio, scale, big, active, marker }); + } + + /// + /// Invokes the GetById(int, string, Color, decimal, double?, float, long, bool, char) Fetch operation of . + /// + public static global::TestApp.PersonEdit GetById(this global::Csla.IDataPortal portal, int id = 5, string? name = "x\"y", global::TestApp.Color color = global::TestApp.Color.Green, decimal amount = 1.5M, double? ratio = null, float scale = 2.25F, long big = -3L, bool active = true, char marker = 'a') + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperation("Fetch__Int32_String_Color_Decimal_Nullable_1_Double_Single_Int64_Boolean_Char", false, new object?[] { id, name, color, amount, ratio, scale, big, active, marker }); + return portal.Fetch(new object?[] { id, name, color, amount, ratio, scale, big, active, marker }); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.DuplicateExtension#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.DuplicateExtension#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs new file mode 100644 index 0000000000..e4a2bf9cc6 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.DuplicateExtension#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs @@ -0,0 +1,32 @@ +//HintName: TestApp.PersonEdit.DataPortalExtensions.g.cs +// +#nullable enable + +namespace TestApp +{ + /// + /// Strongly typed data portal extension methods for . + /// + public static partial class PersonEditDataPortalExtensions + { + /// + /// Invokes the Get(int) Fetch operation of . + /// + public static global::System.Threading.Tasks.Task GetAsync(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperationAsync("Fetch__Int32", false, new object?[] { id }); + return portal.FetchAsync(new object?[] { id }); + } + + /// + /// Invokes the Get(int) Fetch operation of . + /// + public static global::TestApp.PersonEdit Get(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperation("Fetch__Int32", false, new object?[] { id }); + return portal.Fetch(new object?[] { id }); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.DuplicateExtension.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.DuplicateExtension.verified.txt new file mode 100644 index 0000000000..651e6861ed --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.DuplicateExtension.verified.txt @@ -0,0 +1,32 @@ +{ + Diagnostics: [ + { + Location: Source0.cs: (11,17)-(11,20), + Message: The data portal extension method 'GetAsync' for 'PersonEdit' is not generated for 'Get(int, IServiceProvider)' because another operation method already produces an extension method with the same signature, + Severity: Warning, + WarningLevel: 1, + Descriptor: { + Id: CSLADP007, + Title: Generated data portal extension would be duplicated, + MessageFormat: The data portal extension method '{0}' for '{1}' is not generated for '{2}' because another operation method already produces an extension method with the same signature, + Category: Csla.DataPortal, + DefaultSeverity: Warning, + IsEnabledByDefault: true + } + }, + { + Location: Source0.cs: (11,17)-(11,20), + Message: The data portal extension method 'Get' for 'PersonEdit' is not generated for 'Get(int, IServiceProvider)' because another operation method already produces an extension method with the same signature, + Severity: Warning, + WarningLevel: 1, + Descriptor: { + Id: CSLADP007, + Title: Generated data portal extension would be duplicated, + MessageFormat: The data portal extension method '{0}' for '{1}' is not generated for '{2}' because another operation method already produces an extension method with the same signature, + Category: Csla.DataPortal, + DefaultSeverity: Warning, + IsEnabledByDefault: true + } + } + ] +} \ No newline at end of file diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ExecuteCommand#TestApp.RecalculateCommand.DataPortalExtensions.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ExecuteCommand#TestApp.RecalculateCommand.DataPortalExtensions.g.verified.cs new file mode 100644 index 0000000000..891dc2ac24 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ExecuteCommand#TestApp.RecalculateCommand.DataPortalExtensions.g.verified.cs @@ -0,0 +1,32 @@ +//HintName: TestApp.RecalculateCommand.DataPortalExtensions.g.cs +// +#nullable enable + +namespace TestApp +{ + /// + /// Strongly typed data portal extension methods for . + /// + public static partial class RecalculateCommandDataPortalExtensions + { + /// + /// Invokes the Run(int) Execute operation of . + /// + public static global::System.Threading.Tasks.Task RunAsync(this global::Csla.IDataPortal portal, int count) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.ExecuteByOperationAsync("Execute__Int32", false, new object?[] { count }); + return portal.ExecuteAsync(new object?[] { count }); + } + + /// + /// Invokes the Run(int) Execute operation of . + /// + public static global::TestApp.RecalculateCommand Run(this global::Csla.IDataPortal portal, int count) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.ExecuteByOperation("Execute__Int32", false, new object?[] { count }); + return portal.Execute(new object?[] { count }); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.GenericTypeSkipped.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.GenericTypeSkipped.verified.txt new file mode 100644 index 0000000000..48c9182e07 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.GenericTypeSkipped.verified.txt @@ -0,0 +1,18 @@ +{ + Diagnostics: [ + { + Location: Source0.cs: (5,23)-(5,29), + Message: Data portal extension methods are not generated for 'Lookup' because it is generic or nested in a generic type, + Severity: Info, + WarningLevel: 1, + Descriptor: { + Id: CSLADP003, + Title: Data portal extensions are not generated for generic types, + MessageFormat: Data portal extension methods are not generated for '{0}' because it is generic or nested in a generic type, + Category: Csla.DataPortal, + DefaultSeverity: Info, + IsEnabledByDefault: true + } + } + ] +} \ No newline at end of file diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.HiddenNames#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.HiddenNames#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs new file mode 100644 index 0000000000..4539451731 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.HiddenNames#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs @@ -0,0 +1,32 @@ +//HintName: TestApp.PersonEdit.DataPortalExtensions.g.cs +// +#nullable enable + +namespace TestApp +{ + /// + /// Strongly typed data portal extension methods for . + /// + public static partial class PersonEditDataPortalExtensions + { + /// + /// Invokes the GetByCode(string, int) Fetch operation of . + /// + public static global::System.Threading.Tasks.Task GetByCodeAsync(this global::Csla.IDataPortal portal, string code, int version) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperationAsync("Fetch__String_Int32", false, new object?[] { code, version }); + return portal.FetchAsync(new object?[] { code, version }); + } + + /// + /// Invokes the GetByCode(string, int) Fetch operation of . + /// + public static global::TestApp.PersonEdit GetByCode(this global::Csla.IDataPortal portal, string code, int version) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperation("Fetch__String_Int32", false, new object?[] { code, version }); + return portal.Fetch(new object?[] { code, version }); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.HiddenNames.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.HiddenNames.verified.txt new file mode 100644 index 0000000000..80de2c70b0 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.HiddenNames.verified.txt @@ -0,0 +1,60 @@ +{ + Diagnostics: [ + { + Location: Source0.cs: (9,17)-(9,22), + Message: The data portal extension method 'FetchAsync' for 'PersonEdit' is not generated because it would be hidden by the 'IDataPortal.FetchAsync' instance method; set DataPortalExtensions.Prefix to give the generated methods distinct names, + Severity: Warning, + WarningLevel: 1, + Descriptor: { + Id: CSLADP005, + Title: Generated data portal extension would be hidden, + MessageFormat: The data portal extension method '{0}' for '{1}' is not generated because it would be hidden by the '{2}' instance method; set DataPortalExtensions.Prefix to give the generated methods distinct names, + Category: Csla.DataPortal, + DefaultSeverity: Warning, + IsEnabledByDefault: true + } + }, + { + Location: Source0.cs: (9,17)-(9,22), + Message: The data portal extension method 'Fetch' for 'PersonEdit' is not generated because it would be hidden by the 'IDataPortal.Fetch' instance method; set DataPortalExtensions.Prefix to give the generated methods distinct names, + Severity: Warning, + WarningLevel: 1, + Descriptor: { + Id: CSLADP005, + Title: Generated data portal extension would be hidden, + MessageFormat: The data portal extension method '{0}' for '{1}' is not generated because it would be hidden by the '{2}' instance method; set DataPortalExtensions.Prefix to give the generated methods distinct names, + Category: Csla.DataPortal, + DefaultSeverity: Warning, + IsEnabledByDefault: true + } + }, + { + Location: Source0.cs: (12,17)-(12,27), + Message: The data portal extension method 'FetchAsync' for 'PersonEdit' is not generated because it would be hidden by the 'IDataPortal.FetchAsync' instance method; set DataPortalExtensions.Prefix to give the generated methods distinct names, + Severity: Warning, + WarningLevel: 1, + Descriptor: { + Id: CSLADP005, + Title: Generated data portal extension would be hidden, + MessageFormat: The data portal extension method '{0}' for '{1}' is not generated because it would be hidden by the '{2}' instance method; set DataPortalExtensions.Prefix to give the generated methods distinct names, + Category: Csla.DataPortal, + DefaultSeverity: Warning, + IsEnabledByDefault: true + } + }, + { + Location: Source0.cs: (12,17)-(12,27), + Message: The data portal extension method 'Fetch' for 'PersonEdit' is not generated because it would be hidden by the 'IDataPortal.Fetch' instance method; set DataPortalExtensions.Prefix to give the generated methods distinct names, + Severity: Warning, + WarningLevel: 1, + Descriptor: { + Id: CSLADP005, + Title: Generated data portal extension would be hidden, + MessageFormat: The data portal extension method '{0}' for '{1}' is not generated because it would be hidden by the '{2}' instance method; set DataPortalExtensions.Prefix to give the generated methods distinct names, + Category: Csla.DataPortal, + DefaultSeverity: Warning, + IsEnabledByDefault: true + } + } + ] +} \ No newline at end of file diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InternalType#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InternalType#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs new file mode 100644 index 0000000000..c7be478bfe --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InternalType#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs @@ -0,0 +1,32 @@ +//HintName: TestApp.PersonEdit.DataPortalExtensions.g.cs +// +#nullable enable + +namespace TestApp +{ + /// + /// Strongly typed data portal extension methods for . + /// + internal static partial class PersonEditDataPortalExtensions + { + /// + /// Invokes the GetById(int) Fetch operation of . + /// + internal static global::System.Threading.Tasks.Task GetByIdAsync(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperationAsync("Fetch__Int32", false, new object?[] { id }); + return portal.FetchAsync(new object?[] { id }); + } + + /// + /// Invokes the GetById(int) Fetch operation of . + /// + internal static global::TestApp.PersonEdit GetById(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperation("Fetch__Int32", false, new object?[] { id }); + return portal.Fetch(new object?[] { id }); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidTargets.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidTargets.verified.txt new file mode 100644 index 0000000000..4a2bde06ab --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidTargets.verified.txt @@ -0,0 +1,32 @@ +{ + Diagnostics: [ + { + Location: Source0.cs: (5,32)-(5,42), + Message: Data portal extension methods are not generated for 'PersonBase': the type is abstract, + Severity: Warning, + WarningLevel: 1, + Descriptor: { + Id: CSLADP004, + Title: Invalid target for DataPortalExtensions, + MessageFormat: Data portal extension methods are not generated for '{0}': {1}, + Category: Csla.DataPortal, + DefaultSeverity: Warning, + IsEnabledByDefault: true + } + }, + { + Location: Source0.cs: (12,15)-(12,33), + Message: Data portal extension methods are not generated for 'NotABusinessObject': the type does not implement Csla.Core.ICslaObject, + Severity: Warning, + WarningLevel: 1, + Descriptor: { + Id: CSLADP004, + Title: Invalid target for DataPortalExtensions, + MessageFormat: Data portal extension methods are not generated for '{0}': {1}, + Category: Csla.DataPortal, + DefaultSeverity: Warning, + IsEnabledByDefault: true + } + } + ] +} \ No newline at end of file diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.NestedType#TestApp.Outer.PersonEdit.DataPortalExtensions.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.NestedType#TestApp.Outer.PersonEdit.DataPortalExtensions.g.verified.cs new file mode 100644 index 0000000000..100695840c --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.NestedType#TestApp.Outer.PersonEdit.DataPortalExtensions.g.verified.cs @@ -0,0 +1,32 @@ +//HintName: TestApp.Outer.PersonEdit.DataPortalExtensions.g.cs +// +#nullable enable + +namespace TestApp +{ + /// + /// Strongly typed data portal extension methods for . + /// + public static partial class Outer_PersonEditDataPortalExtensions + { + /// + /// Invokes the GetById(int) Fetch operation of . + /// + public static global::System.Threading.Tasks.Task GetByIdAsync(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperationAsync("Fetch__Int32", false, new object?[] { id }); + return portal.FetchAsync(new object?[] { id }); + } + + /// + /// Invokes the GetById(int) Fetch operation of . + /// + public static global::TestApp.Outer.PersonEdit GetById(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperation("Fetch__Int32", false, new object?[] { id }); + return portal.Fetch(new object?[] { id }); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.NoDataPortalExtension#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.NoDataPortalExtension#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs new file mode 100644 index 0000000000..75d40fc521 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.NoDataPortalExtension#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs @@ -0,0 +1,32 @@ +//HintName: TestApp.PersonEdit.DataPortalExtensions.g.cs +// +#nullable enable + +namespace TestApp +{ + /// + /// Strongly typed data portal extension methods for . + /// + public static partial class PersonEditDataPortalExtensions + { + /// + /// Invokes the GetById(int) Fetch operation of . + /// + public static global::System.Threading.Tasks.Task GetByIdAsync(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperationAsync("Fetch__Int32", false, new object?[] { id }); + return portal.FetchAsync(new object?[] { id }); + } + + /// + /// Invokes the GetById(int) Fetch operation of . + /// + public static global::TestApp.PersonEdit GetById(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperation("Fetch__Int32", false, new object?[] { id }); + return portal.Fetch(new object?[] { id }); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ParameterAccessibility#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ParameterAccessibility#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs new file mode 100644 index 0000000000..ec21f4212f --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ParameterAccessibility#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs @@ -0,0 +1,32 @@ +//HintName: TestApp.PersonEdit.DataPortalExtensions.g.cs +// +#nullable enable + +namespace TestApp +{ + /// + /// Strongly typed data portal extension methods for . + /// + public static partial class PersonEditDataPortalExtensions + { + /// + /// Invokes the GetByInternal(InternalCriteria) Fetch operation of . + /// + internal static global::System.Threading.Tasks.Task GetByInternalAsync(this global::Csla.IDataPortal portal, global::TestApp.InternalCriteria criteria) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperationAsync("Fetch__InternalCriteria", false, new object?[] { criteria }); + return portal.FetchAsync(new object?[] { criteria }); + } + + /// + /// Invokes the GetByInternal(InternalCriteria) Fetch operation of . + /// + internal static global::TestApp.PersonEdit GetByInternal(this global::Csla.IDataPortal portal, global::TestApp.InternalCriteria criteria) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperation("Fetch__InternalCriteria", false, new object?[] { criteria }); + return portal.Fetch(new object?[] { criteria }); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ParameterAccessibility.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ParameterAccessibility.verified.txt new file mode 100644 index 0000000000..3510e712ea --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ParameterAccessibility.verified.txt @@ -0,0 +1,17 @@ +{ + Diagnostics: [ + { + Location: Source0.cs: (15,17)-(15,29), + Message: The data portal extension method for 'GetByPrivate(PrivateCriteria)' cannot be generated because parameter 'criteria' has type 'global::TestApp.PersonEdit.PrivateCriteria', which is not accessible outside its containing type, + Severity: Error, + Descriptor: { + Id: CSLADP006, + Title: Operation parameter type is not accessible, + MessageFormat: The data portal extension method for '{0}' cannot be generated because parameter '{1}' has type '{2}', which is not accessible outside its containing type, + Category: Csla.DataPortal, + DefaultSeverity: Error, + IsEnabledByDefault: true + } + } + ] +} \ No newline at end of file diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.Prefix#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.Prefix#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs new file mode 100644 index 0000000000..fc1e85e255 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.Prefix#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs @@ -0,0 +1,52 @@ +//HintName: TestApp.PersonEdit.DataPortalExtensions.g.cs +// +#nullable enable + +namespace TestApp +{ + /// + /// Strongly typed data portal extension methods for . + /// + public static partial class PersonEditDataPortalExtensions + { + /// + /// Invokes the Create() Create operation of . + /// + public static global::System.Threading.Tasks.Task PortalCreateAsync(this global::Csla.IDataPortal portal) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.CreateByOperationAsync("Create", false, global::System.Array.Empty()); + return portal.CreateAsync(); + } + + /// + /// Invokes the Create() Create operation of . + /// + public static global::TestApp.PersonEdit PortalCreate(this global::Csla.IDataPortal portal) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.CreateByOperation("Create", false, global::System.Array.Empty()); + return portal.Create(); + } + + /// + /// Invokes the Fetch(int) Fetch operation of . + /// + public static global::System.Threading.Tasks.Task PortalFetchAsync(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperationAsync("Fetch__Int32", false, new object?[] { id }); + return portal.FetchAsync(new object?[] { id }); + } + + /// + /// Invokes the Fetch(int) Fetch operation of . + /// + public static global::TestApp.PersonEdit PortalFetch(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperation("Fetch__Int32", false, new object?[] { id }); + return portal.Fetch(new object?[] { id }); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.RootOperations#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.RootOperations#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs new file mode 100644 index 0000000000..cb9a6b747d --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.RootOperations#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs @@ -0,0 +1,93 @@ +//HintName: TestApp.PersonEdit.DataPortalExtensions.g.cs +// +#nullable enable + +namespace TestApp +{ + /// + /// Strongly typed data portal extension methods for . + /// + public static partial class PersonEditDataPortalExtensions + { + /// + /// Invokes the New() Create operation of . + /// + public static global::System.Threading.Tasks.Task NewAsync(this global::Csla.IDataPortal portal) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.CreateByOperationAsync("Create", false, global::System.Array.Empty()); + return portal.CreateAsync(); + } + + /// + /// Invokes the New() Create operation of . + /// + public static global::TestApp.PersonEdit New(this global::Csla.IDataPortal portal) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.CreateByOperation("Create", false, global::System.Array.Empty()); + return portal.Create(); + } + + /// + /// Invokes the GetById(int, IDal) Fetch operation of . + /// + public static global::System.Threading.Tasks.Task GetByIdAsync(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperationAsync("Fetch__Int32", false, new object?[] { id }); + return portal.FetchAsync(new object?[] { id }); + } + + /// + /// Invokes the GetById(int, IDal) Fetch operation of . + /// + public static global::TestApp.PersonEdit GetById(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperation("Fetch__Int32", false, new object?[] { id }); + return portal.Fetch(new object?[] { id }); + } + + /// + /// Invokes the RemoveAsync(int) Delete operation of . + /// + public static global::System.Threading.Tasks.Task RemoveAsync(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.DeleteByOperationAsync("Delete__Int32", false, new object?[] { id }); + return portal.DeleteAsync(new object?[] { id }); + } + + /// + /// Invokes the RemoveAsync(int) Delete operation of . + /// + public static void Remove(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + __invoker.DeleteByOperation("Delete__Int32", false, new object?[] { id }); + else + portal.Delete(new object?[] { id }); + } + + /// + /// Invokes the GetLocal(IDal, string) Fetch operation of . + /// + public static global::System.Threading.Tasks.Task GetLocalAsync(this global::Csla.IDataPortal portal, string code) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperationAsync("Fetch__String", true, new object?[] { code }); + return portal.FetchAsync(new object?[] { code }); + } + + /// + /// Invokes the GetLocal(IDal, string) Fetch operation of . + /// + public static global::TestApp.PersonEdit GetLocal(this global::Csla.IDataPortal portal, string code) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperation("Fetch__String", true, new object?[] { code }); + return portal.Fetch(new object?[] { code }); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsTestHelper.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsTestHelper.cs index 9521d2259b..abba5b8752 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsTestHelper.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsTestHelper.cs @@ -4,6 +4,7 @@ // Website: https://cslanet.com // //----------------------------------------------------------------------- +using System.Runtime.CompilerServices; using FluentAssertions; using FluentAssertions.Execution; using Microsoft.CodeAnalysis; @@ -18,7 +19,7 @@ namespace Csla.Generator.AutoImplementProperties.CSharp.Tests.DataPortalOperatio /// asserting the generator reports no diagnostics and the resulting /// compilation has no errors or warnings. /// - public static Task Verify(string source, IEnumerable? additionalSources = null) + public static Task Verify(string source, IEnumerable? additionalSources = null, [CallerFilePath] string sourceFile = "") { var (driver, outputCompilation, diagnostics) = Run(source, additionalSources); @@ -31,14 +32,14 @@ public static Task Verify(string source, IEnumerable? additionalSources diagnostics.Should().BeEmpty(); } - return Verifier.Verify(driver).UseDirectory("Snapshots"); + return Verifier.Verify(driver, sourceFile: sourceFile).UseDirectory("Snapshots"); } /// /// Runs the generator and verifies the output, including any generator /// diagnostics, against snapshots. The resulting compilation must have no errors. /// - public static Task VerifyWithDiagnostics(string source, IEnumerable? additionalSources = null) + public static Task VerifyWithDiagnostics(string source, IEnumerable? additionalSources = null, [CallerFilePath] string sourceFile = "") { var (driver, outputCompilation, _) = Run(source, additionalSources); @@ -46,7 +47,7 @@ public static Task VerifyWithDiagnostics(string source, IEnumerable? add .Where(d => d.Severity == DiagnosticSeverity.Error) .Should().BeEmpty(); - return Verifier.Verify(driver).UseDirectory("Snapshots"); + return Verifier.Verify(driver, sourceFile: sourceFile).UseDirectory("Snapshots"); } /// diff --git a/Source/tests/csla.netcore.test/DataPortal/DataPortalExtensionsTests.cs b/Source/tests/csla.netcore.test/DataPortal/DataPortalExtensionsTests.cs new file mode 100644 index 0000000000..fd913fe888 --- /dev/null +++ b/Source/tests/csla.netcore.test/DataPortal/DataPortalExtensionsTests.cs @@ -0,0 +1,560 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// Integration tests for generated data portal operations and extensions +//----------------------------------------------------------------------- +using System.Collections.Concurrent; +using System.Reflection; +using Csla.Channels.Local; +using Csla.Core; +using Csla.DataPortalClient; +using Csla.Server; +using Csla.Testing; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Csla.Test.DataPortal +{ + [TestClass] + public partial class DataPortalExtensionsTests + { + private static CslaTestHost _testHost = default!; + + [ClassInitialize] + public static void ClassSetup(TestContext context) + { + _ = context; + _testHost = CslaTestHost.Create(options => options.ConfigureServices(services => + { + services.AddSingleton(); + services.AddTransient(sp => new CountingRemoteProxy(sp.GetRequiredService())); + })); + } + + [ClassCleanup] + public static void ClassCleanup() + { + _testHost?.Dispose(); + } + + [TestInitialize] + public void TestSetup() + { + CountingRemoteProxy.Reset(); + } + + #region Generated extensions + + [TestMethod] + public async Task CreateAsync_UsesNamedDispatch() + { + var obj = await _testHost.GetDataPortal().PortalNewAsync(); + + obj.Dispatch.Should().Be(DispatchPath.Named); + obj.Name.Should().Be("new"); + } + + [TestMethod] + public void Create_Sync_UsesNamedDispatch() + { + var obj = _testHost.GetDataPortal().PortalNewNamed("abc"); + + obj.Dispatch.Should().Be(DispatchPath.Named); + obj.Name.Should().Be("abc"); + } + + [TestMethod] + public async Task FetchAsync_UsesNamedDispatchAndInjectsServices() + { + var obj = await _testHost.GetDataPortal().PortalGetByIdAsync(42); + + obj.Dispatch.Should().Be(DispatchPath.Named); + obj.Id.Should().Be(42); + obj.Name.Should().Be(ExtensionTestDal.Marker); + obj.IsNew.Should().BeFalse(); + } + + [TestMethod] + public void Fetch_Sync_UsesNamedDispatch() + { + var obj = _testHost.GetDataPortal().PortalGetById(7); + + obj.Dispatch.Should().Be(DispatchPath.Named); + obj.Id.Should().Be(7); + } + + [TestMethod] + public async Task FetchAsync_StringArrayCriteria_IsNotSplit() + { + var obj = await _testHost.GetDataPortal().PortalGetByNamesAsync(["a", "b", "c"]); + + obj.Dispatch.Should().Be(DispatchPath.Named); + obj.Name.Should().Be("a,b,c"); + } + + [TestMethod] + public async Task FetchAsync_NullableCriteria_AcceptsNull() + { + var obj = await _testHost.GetDataPortal().PortalGetByOptionalAsync(null, null); + + obj.Dispatch.Should().Be(DispatchPath.Named); + obj.Name.Should().Be("|"); + } + + [TestMethod] + public async Task FetchAsync_AsyncOperationMethod_UsesNamedDispatch() + { + var key = Guid.NewGuid(); + var obj = await _testHost.GetDataPortal().PortalLoadAsync(key); + + obj.Dispatch.Should().Be(DispatchPath.Named); + obj.Name.Should().Be(key.ToString()); + } + + [TestMethod] + public void Fetch_Sync_AsyncOperationMethod_ThrowsLikeReflectionDispatch() + { + // a local data portal leaves the execution location as client + using var localHost = CslaTestHost.Create(); + var generated = () => localHost.GetDataPortal().PortalLoad(Guid.NewGuid()); + var reflection = () => localHost.GetDataPortal().Fetch(Guid.NewGuid()); + + var generatedException = generated.Should().Throw().Which; + var reflectionException = reflection.Should().Throw().Which; + + generatedException.GetType().Should().Be(reflectionException.GetType()); + generatedException.GetBaseException().Should().BeOfType(); + reflectionException.GetBaseException().Should().BeOfType(); + } + + [TestMethod] + public async Task FetchAsync_NonRunLocal_UsesConfiguredProxy() + { + _ = await _testHost.GetDataPortal().PortalGetByIdAsync(1); + + CountingRemoteProxy.Calls.Should().Be(1); + } + + [TestMethod] + public async Task FetchAsync_RunLocal_BypassesConfiguredProxy() + { + var obj = await _testHost.GetDataPortal().PortalGetLocalAsync("local".ToCharArray()); + + obj.Dispatch.Should().Be(DispatchPath.Named); + obj.Name.Should().Be("local"); + CountingRemoteProxy.Calls.Should().Be(0); + } + + [TestMethod] + public async Task DeleteAsync_UsesNamedDispatch() + { + await _testHost.GetDataPortal().PortalRemoveAsync(11); + + ExtensionRoot.Deleted.TryRemove(11, out var dispatch).Should().BeTrue(); + dispatch.Should().Be(DispatchPath.Named); + } + + [TestMethod] + public void Delete_Sync_UsesNamedDispatch() + { + _testHost.GetDataPortal().PortalRemove(12); + + ExtensionRoot.Deleted.TryRemove(12, out var dispatch).Should().BeTrue(); + dispatch.Should().Be(DispatchPath.Named); + } + + [TestMethod] + public async Task ExecuteAsync_UsesNamedDispatch() + { + var cmd = await _testHost.GetDataPortal().PortalRunAsync(3); + + cmd.Dispatch.Should().Be(DispatchPath.Named); + cmd.Result.Should().Be(6); + } + + [TestMethod] + public void Execute_Sync_UsesNamedDispatch() + { + var cmd = _testHost.GetDataPortal().PortalRun(4); + + cmd.Dispatch.Should().Be(DispatchPath.Named); + cmd.Result.Should().Be(8); + } + + [TestMethod] + public async Task CreateChildAsync_UsesNamedDispatch() + { + var child = await _testHost.GetChildDataPortal().PortalNewChildAsync("kid"); + + child.Dispatch.Should().Be(DispatchPath.Named); + child.Name.Should().Be("kid"); + child.IsChild.Should().BeTrue(); + child.IsNew.Should().BeTrue(); + } + + [TestMethod] + public void CreateChild_Sync_UsesNamedDispatch() + { + var child = _testHost.GetChildDataPortal().PortalNewChild("kid"); + + child.Dispatch.Should().Be(DispatchPath.Named); + } + + [TestMethod] + public async Task FetchChildAsync_UsesNamedDispatch() + { + var child = await _testHost.GetChildDataPortal().PortalLoadChildAsync(9, ["x", "y"]); + + child.Dispatch.Should().Be(DispatchPath.Named); + child.Name.Should().Be("9:x,y"); + child.IsNew.Should().BeFalse(); + } + + [TestMethod] + public void FetchChild_Sync_UsesNamedDispatch() + { + var child = _testHost.GetChildDataPortal().PortalLoadChild(10, ["z"]); + + child.Dispatch.Should().Be(DispatchPath.Named); + child.Name.Should().Be("10:z"); + } + + [TestMethod] + public async Task ExistingParamsApi_StillUsesGeneratedDispatch() + { + var obj = await _testHost.GetDataPortal().FetchAsync(3); + + obj.Dispatch.Should().Be(DispatchPath.Named); + obj.Id.Should().Be(3); + } + + [TestMethod] + public async Task ChildParamsApi_UsesTypeBasedDispatch() + { + var child = await _testHost.GetChildDataPortal().CreateChildAsync("kid"); + + child.Dispatch.Should().Be(DispatchPath.Type); + } + + [TestMethod] + public async Task CustomDataPortal_UsesFallback() + { + var fake = new FakeDataPortal(); + + var obj = await fake.PortalGetByIdAsync(99); + + obj.Should().BeSameAs(fake.Result); + fake.FetchCriteria.Should().Equal(99); + } + + [TestMethod] + public async Task CustomDataPortal_UsesFallbackWithoutSplittingArrays() + { + var fake = new FakeDataPortal(); + string[] names = ["a", "b"]; + + _ = await fake.PortalGetByNamesAsync(names); + + fake.FetchCriteria.Should().HaveCount(1); + fake.FetchCriteria![0].Should().BeSameAs(names); + } + + #endregion + + #region Name consistency + + [TestMethod] + public void GeneratedInterfaceMemberNames_MatchRuntimeOperationNames() + { + var checkedMembers = 0; + var types = typeof(DataPortalExtensionsTests).Assembly.GetTypes() + .Where(t => !t.ContainsGenericParameters) + .Select(t => (Type: t, Operations: t.GetNestedType("IDataPortalOperations", BindingFlags.NonPublic | BindingFlags.Public))) + .Where(t => t.Operations is not null) + .ToList(); + + types.Should().NotBeEmpty(); + + foreach (var (type, operations) in types) + { + foreach (var member in operations!.GetMethods()) + { + var kind = member.Name.Split('_')[0]; + var attributeType = typeof(FetchAttribute).Assembly.GetType($"Csla.{kind}Attribute", throwOnError: true)!; + var memberParameters = member.GetParameters().Select(p => p.ParameterType).ToArray(); + + var method = type + .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly) + .Single(m => m.GetCustomAttributes(attributeType, false).Any() + && m.GetParameters().Select(p => p.ParameterType).SequenceEqual(memberParameters)); + + var expected = (string)typeof(DataPortalOperationNameHelper) + .GetMethod(nameof(DataPortalOperationNameHelper.ComputeOperationName), BindingFlags.Static | BindingFlags.NonPublic)! + .MakeGenericMethod(attributeType) + .Invoke(null, [method])!; + + member.Name.Should().Match(name => name == expected || name.StartsWith(expected + "_"), + $"{type.Name}.{method.Name} should map to operation {expected}"); + checkedMembers++; + } + } + + checkedMembers.Should().BeGreaterThan(10); + } + + #endregion + + #region Test types + + public static class DispatchPath + { + public const string Named = "Named"; + public const string Type = "Type"; + public const string Reflection = "Reflection"; + + public static string Current() + { + var stackTrace = Environment.StackTrace; + if (stackTrace.Contains(nameof(IDataPortalOperationNamedMapping))) + return Named; + if (stackTrace.Contains(nameof(IDataPortalOperationMapping))) + return Type; + return Reflection; + } + } + + public class ExtensionTestDal + { + public const string Marker = "from-dal"; + } + + public class CountingRemoteProxy(LocalProxy implementingProxy) : IDataPortalProxy + { + private static int _calls; + + public static int Calls => _calls; + + public static void Reset() => _calls = 0; + + public bool IsServerRemote => true; + + public Task Create(Type objectType, object criteria, DataPortalContext context, bool isSync) + { + Interlocked.Increment(ref _calls); + return implementingProxy.Create(objectType, criteria, context, isSync); + } + + public Task Delete(Type objectType, object criteria, DataPortalContext context, bool isSync) + { + Interlocked.Increment(ref _calls); + return implementingProxy.Delete(objectType, criteria, context, isSync); + } + + public Task Fetch(Type objectType, object criteria, DataPortalContext context, bool isSync) + { + Interlocked.Increment(ref _calls); + return implementingProxy.Fetch(objectType, criteria, context, isSync); + } + + public Task Update(ICslaObject obj, DataPortalContext context, bool isSync) + { + Interlocked.Increment(ref _calls); + return implementingProxy.Update(obj, context, isSync); + } + } + + [DataPortalExtensions(Prefix = "Portal")] + public partial class ExtensionRoot : BusinessBase + { + public static readonly ConcurrentDictionary Deleted = new(); + + public static readonly PropertyInfo DispatchProperty = RegisterProperty(nameof(Dispatch)); + public string Dispatch + { + get => GetProperty(DispatchProperty); + private set => LoadProperty(DispatchProperty, value); + } + + public static readonly PropertyInfo IdProperty = RegisterProperty(nameof(Id)); + public int Id + { + get => GetProperty(IdProperty); + private set => LoadProperty(IdProperty, value); + } + + public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); + public string Name + { + get => GetProperty(NameProperty); + private set => LoadProperty(NameProperty, value); + } + + [Create] + private void New() + { + Dispatch = DispatchPath.Current(); + Name = "new"; + } + + [Create] + private void NewNamed(string name) + { + Dispatch = DispatchPath.Current(); + Name = name; + } + + [Fetch] + private void GetById(int id, [Inject] ExtensionTestDal dal) + { + _ = dal; + Dispatch = DispatchPath.Current(); + Id = id; + Name = ExtensionTestDal.Marker; + } + + [Fetch] + private void GetByNames(string[] names) + { + Dispatch = DispatchPath.Current(); + Name = string.Join(",", names); + } + + [Fetch] + private void GetByOptional(int? number, string? text) + { + Dispatch = DispatchPath.Current(); + Name = $"{number?.ToString() ?? ""}|{text ?? ""}"; + } + + [Fetch] + private async Task LoadAsync(Guid key) + { + Dispatch = DispatchPath.Current(); + await Task.Yield(); + Name = key.ToString(); + } + + [RunLocal] + [Fetch] + private void GetLocal(char[] code) + { + Dispatch = DispatchPath.Current(); + Name = new string(code); + } + + [Delete] + private void Remove(int id) + { + Deleted[id] = DispatchPath.Current(); + } + + [Fetch] + private void GetByKey((int Id, string Name) key, List values, Dictionary[] map) + { + _ = key; + _ = values; + _ = map; + } + } + + /// + /// Not partial, so dispatch uses reflection. + /// + public class ReflectionRoot : BusinessBase + { + [Fetch] + private async Task LoadAsync(Guid key) + { + _ = key; + await Task.Yield(); + } + } + + [DataPortalExtensions(Prefix = "Portal")] + public partial class ExtensionCommand : CommandBase + { + public static readonly PropertyInfo DispatchProperty = RegisterProperty(nameof(Dispatch)); + public string Dispatch + { + get => ReadProperty(DispatchProperty); + private set => LoadProperty(DispatchProperty, value); + } + + public static readonly PropertyInfo ResultProperty = RegisterProperty(nameof(Result)); + public int Result + { + get => ReadProperty(ResultProperty); + private set => LoadProperty(ResultProperty, value); + } + + [Execute] + private void Run(int value) + { + Dispatch = DispatchPath.Current(); + Result = value * 2; + } + } + + [DataPortalExtensions(Prefix = "Portal")] + public partial class ExtensionChild : BusinessBase + { + public static readonly PropertyInfo DispatchProperty = RegisterProperty(nameof(Dispatch)); + public string Dispatch + { + get => GetProperty(DispatchProperty); + private set => LoadProperty(DispatchProperty, value); + } + + public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); + public string Name + { + get => GetProperty(NameProperty); + private set => LoadProperty(NameProperty, value); + } + + [CreateChild] + private void NewChild(string name) + { + Dispatch = DispatchPath.Current(); + Name = name; + } + + [FetchChild] + private void LoadChild(int id, string[] tags) + { + Dispatch = DispatchPath.Current(); + Name = $"{id}:{string.Join(",", tags)}"; + } + } + + private sealed class FakeDataPortal : IDataPortal + { + public ExtensionRoot Result { get; } = new(); + + public object?[]? FetchCriteria { get; private set; } + + public Task FetchAsync(params object?[]? criteria) + { + FetchCriteria = criteria; + return Task.FromResult(Result); + } + + public Task CreateAsync(params object?[]? criteria) => throw new NotSupportedException(); + public Task UpdateAsync(ExtensionRoot obj) => throw new NotSupportedException(); + public Task ExecuteAsync(ExtensionRoot command) => throw new NotSupportedException(); + public Task ExecuteAsync(params object?[]? criteria) => throw new NotSupportedException(); + public Task DeleteAsync(params object?[]? criteria) => throw new NotSupportedException(); + public ExtensionRoot Create(params object?[]? criteria) => throw new NotSupportedException(); + public ExtensionRoot Fetch(params object?[]? criteria) => throw new NotSupportedException(); + public ExtensionRoot Execute(ExtensionRoot obj) => throw new NotSupportedException(); + public ExtensionRoot Execute(params object?[]? criteria) => throw new NotSupportedException(); + public ExtensionRoot Update(ExtensionRoot obj) => throw new NotSupportedException(); + public void Delete(params object?[]? criteria) => throw new NotSupportedException(); + } + + #endregion + } +} diff --git a/Source/tests/csla.netcore.test/csla.netcore.test.csproj b/Source/tests/csla.netcore.test/csla.netcore.test.csproj index 7fb6952145..eb0e6d96c0 100644 --- a/Source/tests/csla.netcore.test/csla.netcore.test.csproj +++ b/Source/tests/csla.netcore.test/csla.netcore.test.csproj @@ -27,6 +27,7 @@ + diff --git a/docs/code-generators.md b/docs/code-generators.md index e3284151e8..168215b48a 100644 --- a/docs/code-generators.md +++ b/docs/code-generators.md @@ -4,7 +4,7 @@ Starting with CSLA 9, there are code generators available as NuGet packages. These packages are designed to work with the CSLA .NET framework to generate code for your business objects as part of the .NET build process. -* [AutoImplementProperties](https://github.com/MarimerLLC/csla/tree/main/Source/Csla.Generators/cs/AutoImplementProperties) - Uses code generation to radically reduce the amount of code you need to write to declare properties in a business domain class +* [AutoImplementProperties](https://github.com/MarimerLLC/csla/tree/main/Source/Csla.Generators/cs/AutoImplementProperties) - Uses code generation to radically reduce the amount of code you need to write to declare properties in a business domain class. Starting with CSLA 11 this generator is included in the `Csla` package, along with the data portal operations generator (explicit, reflection-free calls to data portal operation methods) and the `[DataPortalExtensions]` generator (strongly typed data portal extension methods). * [AutoSerialization](https://github.com/MarimerLLC/csla/tree/main/Source/Csla.Generators/cs/AutoSerialization) - Uses code generation to improve serialization performance Other modern code generator tools also exist: diff --git a/releasenotes.md b/releasenotes.md index 8d84265e67..ff37f1bee2 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -10,10 +10,44 @@ Primary changes in this release include: * Add support for .NET 11 ([#4699](https://github.com/MarimerLLC/csla/issues/4699)) * Remove support for .NET 8 and .NET 9, which Microsoft stops supporting in November 2026 ([#4348](https://github.com/MarimerLLC/csla/issues/4348), [#4581](https://github.com/MarimerLLC/csla/issues/4581)) +* Call data portal operation methods explicitly instead of through reflection ([#4359](https://github.com/MarimerLLC/csla/issues/4359)) + +### Source generators bundled in the Csla package + +The CSLA source generators now ship inside the `Csla` NuGet package and run automatically in any C# project that references `Csla`. There is no separate generator package to install. + +* The `[CslaImplementProperties]`, `[CslaImplementPropertiesInterface]` and `[CslaIgnoreProperty]` attributes are now part of `Csla.dll`. +* For every `partial` class that declares data portal operation methods (`[Create]`, `[Fetch]`, `[Insert]`, `[Update]`, `[Execute]`, `[Delete]`, `[DeleteSelf]` and the child equivalents), the generator emits an internal nested `IDataPortalOperations` interface that names each operation method, plus implementations of `IDataPortalOperationMapping` and `IDataPortalOperationNamedMapping` for concrete classes. The data portal then calls operation methods directly instead of using reflection, and IDEs, analyzers and the trimmer see private operation methods as used. +* Business classes with data portal operation methods should be declared `partial`. Analyzer **CSLA0024** reports classes (or containing types) that are not partial and provides a code fix that adds the `partial` modifier. Non-partial classes continue to work through reflection. + +### Strongly typed data portal extension methods + +Add `[DataPortalExtensions]` to a business class to generate strongly typed extension methods on `IDataPortal` and `IChildDataPortal` for its `[Create]`, `[Fetch]`, `[Execute]`, `[Delete]`, `[CreateChild]` and `[FetchChild]` methods, in both async and sync forms: + +```csharp +[DataPortalExtensions(Prefix = "Portal")] +public partial class PersonEdit : BusinessBase +{ + [Fetch] + private async Task Fetch(int id, [Inject] IPersonDal dal) { ... } +} + +var person = await portal.PortalFetchAsync(42); +``` + +* Generated methods take only the criteria parameters (`[Inject]` parameters are omitted), and pass the pre-computed operation name and `[RunLocal]` setting to the data portal, so the client no longer searches for the operation method using reflection. +* Because `IDataPortal` instance methods such as `FetchAsync` take precedence over extension methods, a generated name that matches one is not generated and is reported as a warning. Use `Prefix` to give the generated methods distinct names. +* Use `[NoDataPortalExtension]` to exclude an operation method. +* Custom or mock `IDataPortal` implementations continue to work; the generated methods fall back to the existing `params object[]` methods. +* Analyzer **CSLA0025** suggests using the generated extension methods instead of calling `IDataPortal` methods with untyped criteria for classes marked `[DataPortalExtensions]`. + +The data portal extension generator and the CSLA0025 analyzer are adapted from [Csla.DataPortalExtensions](https://github.com/StefanOssendorf/Csla.DataPortalExtensions) by [Stefan Ossendorf](https://github.com/StefanOssendorf), used under the MIT License. Thank you, Stefan! ### Breaking Changes * CSLA 11 no longer targets .NET 8 or .NET 9. Applications must target .NET 10 or later (or continue to use .NET Framework 4.6.2 through 4.8, or a `netstandard2.0`-compatible runtime). +* The `Csla.Generator.AutoImplementProperties.CSharp` package is retired because its generators are now included in the `Csla` package. Remove any `PackageReference` (and `PackageVersion`) for it; a build error (`CSLABUILD001`) is reported while the reference remains. +* Csla's abstract base classes that declare data portal operation methods (`BusinessListBase`, `CommandBase`, `ReadOnlyBase`, and others) are now `partial`. ### Supported Platforms @@ -37,5 +71,10 @@ Also expected to work on: ### Contributors * [@rockfordlhotka](https://github.com/rockfordlhotka) +* [@StefanOssendorf](https://github.com/StefanOssendorf) + +### Third-party notices + +* Portions of the data portal extension generator and analyzers are adapted from [Csla.DataPortalExtensions](https://github.com/StefanOssendorf/Csla.DataPortalExtensions), Copyright (c) 2023 Stefan Ossendorf, licensed under the [MIT License](https://github.com/StefanOssendorf/Csla.DataPortalExtensions/blob/main/LICENSE). Thank you all for your contributions! From e4d1740d429f44694c8d9737bc8bdf2b44af00e0 Mon Sep 17 00:00:00 2001 From: Rockford lhotka Date: Sun, 13 Sep 2026 18:33:41 -0500 Subject: [PATCH 05/10] #4359 Pack buildTransitive Csla.targets with an explicit None item The Update form matched only inner (per-framework) builds, so the outer pack build omitted the retired-package guard from the nupkg. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Dn4aKN7kqcG78x1HBWApk3 --- Source/Csla/Csla.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Csla/Csla.csproj b/Source/Csla/Csla.csproj index ee35afed6b..1ecfe6f1cd 100644 --- a/Source/Csla/Csla.csproj +++ b/Source/Csla/Csla.csproj @@ -116,7 +116,8 @@ - + + True True From 77e2158255ddd8b3b8487bdd840239caa2d1647f Mon Sep 17 00:00:00 2001 From: Rockford lhotka Date: Sun, 13 Sep 2026 23:35:02 -0500 Subject: [PATCH 06/10] #4359 Address PR review feedback on data portal generators - Wrap exceptions from generated operation calls in CallMethodException, matching reflection dispatch; an operation that throws DataPortalOperationNotSupportedException is no longer invoked again - Bind explicit in criteria to a local in named dispatch - Resolve Nullable injected services by their declared type and treat nullable inject parameters as optional, as reflection does - Escape keyword extension names, validate the Prefix (CSLADP008), and choose receiver/invoker names that cannot collide with parameters - Check hidden extension names against the receiving portal interface only - Report inaccessible parameter types as a warning - Keep a single object[] criteria value as one criterion in the invokers - CSLA0025 only reports when a matching extension method is generated Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01W6AuArAiFMmEoBmSNbD8YG --- ...UseGeneratedDataPortalExtensionAnalyzer.cs | 191 +++++++++++++++++- .../DataPortalExtensionsBuilder.cs | 34 +++- .../ExtensionTypeModel.cs | 12 +- ...ncrementalDataPortalExtensionsGenerator.cs | 23 ++- .../DataPortalOperationsBuilder.cs | 28 ++- .../DataPortalOperationsDiagnostics.cs | 12 +- .../OperationDiscovery.cs | 4 + Source/Csla/DataPortalT.cs | 6 +- .../Csla/Server/DataPortalOperationHelper.cs | 18 ++ .../Server/IDataPortalOperationMapping.cs | 5 +- .../IDataPortalOperationNamedMapping.cs | 5 +- ...neratedDataPortalExtensionAnalyzerTests.cs | 123 ++++++++++- .../DataPortalExtensionsGeneratorTests.cs | 63 ++++++ ...sGeneratorTests.InvalidPrefix.verified.txt | 18 ++ ...sonEdit.DataPortalExtensions.g.verified.cs | 32 +++ ...rTests.ParameterAccessibility.verified.txt | 5 +- ...sonEdit.DataPortalExtensions.g.verified.cs | 52 +++++ .../DataPortalOperationsGeneratorTests.cs | 24 +++ ...sonEdit.DataPortalOperations.g.verified.cs | 54 ++++- ...sonEdit.DataPortalOperations.g.verified.cs | 90 ++++++++- ...sonEdit.DataPortalOperations.g.verified.cs | 18 +- ...ineItem.DataPortalOperations.g.verified.cs | 90 ++++++++- ...sonEdit.DataPortalOperations.g.verified.cs | 90 ++++++++- ...sonEdit.DataPortalOperations.g.verified.cs | 18 +- ...sonEdit.DataPortalOperations.g.verified.cs | 18 +- ...sonEdit.DataPortalOperations.g.verified.cs | 18 +- ...Command.DataPortalOperations.g.verified.cs | 18 +- ...sonEdit.DataPortalOperations.g.verified.cs | 9 +- ...sonEdit.DataPortalOperations.g.verified.cs | 36 +++- ...ookup_1.DataPortalOperations.g.verified.cs | 27 ++- ...sonEdit.DataPortalOperations.g.verified.cs | 104 ++++++++++ ...sonEdit.DataPortalOperations.g.verified.cs | 18 +- ...sonEdit.DataPortalOperations.g.verified.cs | 18 +- ...sonEdit.DataPortalOperations.g.verified.cs | 36 +++- ...sonEdit.DataPortalOperations.g.verified.cs | 18 +- ...sonEdit.DataPortalOperations.g.verified.cs | 18 +- ...sonEdit.DataPortalOperations.g.verified.cs | 18 +- ...sonEdit.DataPortalOperations.g.verified.cs | 18 +- ...sonEdit.DataPortalOperations.g.verified.cs | 36 +++- ...sonEdit.DataPortalOperations.g.verified.cs | 36 +++- ...sonEdit.DataPortalOperations.g.verified.cs | 54 ++++- ...sonEdit.DataPortalOperations.g.verified.cs | 18 +- ...sonEdit.DataPortalOperations.g.verified.cs | 18 +- .../DataPortal/DataPortalExtensionsTests.cs | 107 ++++++++++ ...UseGeneratedDataPortalExtensionAnalyzer.md | 2 + 45 files changed, 1531 insertions(+), 129 deletions(-) create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidPrefix.verified.txt create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.KeywordAndCollidingNames#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ReceiverSpecificHiddenNames#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InCriteriaAndNullableInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs diff --git a/Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzer.cs b/Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzer.cs index 3be72c3b6a..bddfb7d98c 100644 --- a/Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzer.cs +++ b/Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzer.cs @@ -3,6 +3,7 @@ // Copyright (c) Stefan Ossendorf, licensed under the MIT License. using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; @@ -15,7 +16,9 @@ namespace Csla.Analyzers /// IDataPortal<T>, IChildDataPortal<T>, and /// DataPortal<T> when T is marked with /// [DataPortalExtensions], meaning strongly typed extension - /// methods are generated and should be used instead. + /// methods are generated and should be used instead. The diagnostic is + /// only reported when the generator produces an extension method that + /// can replace the call. /// [DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class UseGeneratedDataPortalExtensionAnalyzer @@ -56,7 +59,10 @@ public override void Initialize(AnalysisContext context) extensionsAttribute, compilation.GetTypeByMetadataName("Csla.IDataPortal`1"), compilation.GetTypeByMetadataName("Csla.IChildDataPortal`1"), - compilation.GetTypeByMetadataName("Csla.DataPortal`1")); + compilation.GetTypeByMetadataName("Csla.DataPortal`1"), + compilation.GetTypeByMetadataName("Csla.NoDataPortalExtensionAttribute"), + compilation.GetTypeByMetadataName("Csla.InjectAttribute"), + compilation.GetTypeByMetadataName("Csla.Core.ICslaObject")); compilationContext.RegisterOperationAction( operationContext => AnalyzeInvocation(operationContext, symbols), OperationKind.Invocation); @@ -90,7 +96,12 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, KnownSym context.CancellationToken.ThrowIfCancellationRequested(); if (containingType.TypeArguments[0] is not INamedTypeSymbol businessType || - !HasDataPortalExtensionsAttribute(businessType, symbols.DataPortalExtensionsAttribute)) + GetDataPortalExtensionsAttribute(businessType, symbols.DataPortalExtensionsAttribute) is not { } attribute) + { + return; + } + + if (!HasGeneratedExtension(businessType, attribute, method.Name, GetCriteriaCount(invocation), symbols)) { return; } @@ -124,19 +135,181 @@ private static bool IsCriteriaMethod(IMethodSymbol method, bool isRootPortal, bo } } - private static bool HasDataPortalExtensionsAttribute(INamedTypeSymbol businessType, INamedTypeSymbol attributeType) + private static AttributeData? GetDataPortalExtensionsAttribute(INamedTypeSymbol businessType, INamedTypeSymbol attributeType) { foreach (var attribute in businessType.GetAttributes()) { if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, attributeType)) { - return true; + return attribute; + } + } + + return null; + } + + /// + /// Mirrors the data portal extensions generator: returns true when an + /// extension method is generated for an operation method of the kind + /// called that accepts the number of criteria values passed. + /// + private static bool HasGeneratedExtension(INamedTypeSymbol businessType, AttributeData attribute, string portalMethodName, int? criteriaCount, KnownSymbols symbols) + { + if (businessType.IsAbstract || symbols.ICslaObject is null || + !businessType.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, symbols.ICslaObject))) + { + return false; + } + + for (var type = businessType; type is not null; type = type.ContainingType) + { + if (type.TypeParameters.Length > 0) + { + return false; + } + } + + if (IsPrivate(businessType)) + { + return false; + } + + var prefix = attribute.NamedArguments + .Where(a => a.Key == "Prefix") + .Select(a => a.Value.Value as string) + .FirstOrDefault() ?? string.Empty; + if (prefix.Length > 0 && !SyntaxFacts.IsValidIdentifier(prefix)) + { + return false; + } + + var isAsync = portalMethodName.EndsWith("Async", StringComparison.Ordinal); + var kind = isAsync ? portalMethodName.Substring(0, portalMethodName.Length - "Async".Length) : portalMethodName; + var receiver = kind is "CreateChild" or "FetchChild" ? symbols.IChildDataPortal : symbols.IDataPortal; + var hiddenNames = receiver is null + ? ImmutableHashSet.Empty + : receiver.GetMembers().Select(m => m.Name).ToImmutableHashSet(); + var operationAttributeName = $"Csla.{kind}Attribute"; + + foreach (var operation in businessType.GetMembers().OfType()) + { + if (operation.MethodKind != MethodKind.Ordinary || operation.IsStatic || operation.IsGenericMethod) + { + continue; + } + + var attributes = operation.GetAttributes(); + if (!attributes.Any(a => a.AttributeClass?.ToDisplayString() == operationAttributeName) || + attributes.Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, symbols.NoDataPortalExtensionAttribute))) + { + continue; + } + + // ref/out parameters and a single object[] parameter are only invoked through reflection + if (operation.Parameters.Any(p => p.RefKind is RefKind.Ref or RefKind.Out) || + (operation.Parameters.Length == 1 && operation.Parameters[0].Type is IArrayTypeSymbol { Rank: 1, ElementType.SpecialType: SpecialType.System_Object })) + { + continue; + } + + var criteria = operation.Parameters.Where(p => !IsInjected(p, symbols.InjectAttribute)).ToList(); + if (criteria.Any(p => IsPrivate(p.Type))) + { + continue; + } + + var baseName = operation.Name; + if (baseName.EndsWith("Async", StringComparison.Ordinal) && baseName.Length > "Async".Length) + { + baseName = baseName.Substring(0, baseName.Length - "Async".Length); + } + + if (hiddenNames.Contains(prefix + baseName + (isAsync ? "Async" : string.Empty))) + { + continue; } + + if (criteriaCount is { } count) + { + var required = criteria.Count(p => !p.HasExplicitDefaultValue && !p.IsParams); + var hasParams = criteria.Count > 0 && criteria[criteria.Count - 1].IsParams; + if (count < required || (!hasParams && count > criteria.Count)) + { + continue; + } + } + + return true; } return false; } + /// + /// The number of criteria values passed to the data portal method, or + /// null when they are passed as an existing array. + /// + private static int? GetCriteriaCount(IInvocationOperation invocation) + { + if (invocation.Arguments.Length == 0) + { + return 0; + } + + var argument = invocation.Arguments[invocation.Arguments.Length - 1]; + if (argument.ArgumentKind == ArgumentKind.ParamArray && argument.Value is IArrayCreationOperation { Initializer: { } initializer }) + { + return initializer.ElementValues.Length; + } + + return null; + } + + private static bool IsInjected(IParameterSymbol parameter, INamedTypeSymbol? injectAttribute) + { + if (injectAttribute is null) + { + return false; + } + + foreach (var attribute in parameter.GetAttributes()) + { + for (var type = attribute.AttributeClass; type is not null; type = type.BaseType) + { + if (SymbolEqualityComparer.Default.Equals(type, injectAttribute)) + { + return true; + } + } + } + + return false; + } + + /// + /// The type, or a type it is composed of, is not accessible outside its containing type. + /// + private static bool IsPrivate(ITypeSymbol type) + { + switch (type) + { + case IArrayTypeSymbol array: + return IsPrivate(array.ElementType); + case INamedTypeSymbol named: + for (var current = named; current is not null; current = current.ContainingType) + { + if (current.DeclaredAccessibility is not (Accessibility.Public or Accessibility.Internal or Accessibility.ProtectedOrInternal)) + { + return true; + } + } + + return named.TypeArguments.Any(IsPrivate); + default: + return false; + } + } + private static Location GetLocation(IInvocationOperation invocation) { if (invocation.Syntax is InvocationExpressionSyntax invocationSyntax) @@ -157,12 +330,18 @@ private sealed class KnownSymbols( INamedTypeSymbol dataPortalExtensionsAttribute, INamedTypeSymbol? iDataPortal, INamedTypeSymbol? iChildDataPortal, - INamedTypeSymbol? dataPortal) + INamedTypeSymbol? dataPortal, + INamedTypeSymbol? noDataPortalExtensionAttribute, + INamedTypeSymbol? injectAttribute, + INamedTypeSymbol? iCslaObject) { public INamedTypeSymbol DataPortalExtensionsAttribute { get; } = dataPortalExtensionsAttribute; public INamedTypeSymbol? IDataPortal { get; } = iDataPortal; public INamedTypeSymbol? IChildDataPortal { get; } = iChildDataPortal; public INamedTypeSymbol? DataPortal { get; } = dataPortal; + public INamedTypeSymbol? NoDataPortalExtensionAttribute { get; } = noDataPortalExtensionAttribute; + public INamedTypeSymbol? InjectAttribute { get; } = injectAttribute; + public INamedTypeSymbol? ICslaObject { get; } = iCslaObject; } } } diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsBuilder.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsBuilder.cs index 6b218c388a..e1307567d0 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsBuilder.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsBuilder.cs @@ -13,6 +13,7 @@ using System.CodeDom.Compiler; using Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations; using Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations.Models; +using Microsoft.CodeAnalysis.CSharp; namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalExtensions { @@ -23,6 +24,7 @@ namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalExtensions internal static class DataPortalExtensionsBuilder { private const string ReceiverName = "portal"; + private const string InvokerName = "__invoker"; /// /// Build the generated source and diagnostics for a business type. @@ -52,6 +54,12 @@ public static (string? Source, List Diagnostics) Build(Extension return (null, diagnostics); } + if (type.Prefix.Length > 0 && !SyntaxFacts.IsValidIdentifier(type.Prefix)) + { + diagnostics.Add(Diagnostic(DataPortalOperationsDiagnostics.InvalidExtensionPrefixId, type.Location, type.TypeName, type.Prefix)); + return (null, diagnostics); + } + var methods = SelectMethods(model, diagnostics); if (methods.Count == 0) return (null, diagnostics); @@ -125,12 +133,13 @@ private static List SelectMethods(ExtensionGenerationModel mode var isChild = method.Kind is "CreateChild" or "FetchChild"; var receiverInterface = isChild ? "IChildDataPortal" : "IDataPortal"; + var hiddenNames = isChild ? type.ChildHiddenNames : type.RootHiddenNames; var parameterTypes = string.Join(",", criteria.Select(p => p.PatternTypeDisplay + (p.IsNullableValueType ? "?" : string.Empty))); foreach (var isAsync in new[] { true, false }) { var name = type.Prefix + baseName + (isAsync ? "Async" : string.Empty); - if (type.HiddenNames.Contains(name)) + if (hiddenNames.Contains(name)) { diagnostics.Add(Diagnostic(DataPortalOperationsDiagnostics.ExtensionNameHiddenId, method.Location, name, type.TypeName, $"{receiverInterface}.{name}")); continue; @@ -154,7 +163,9 @@ private static void AppendMethod(IndentedTextWriter writer, ExtensionTypeModel t var method = extension.Method; var criteria = method.CriteriaParameters.ToList(); var isDelete = method.Kind == "Delete"; - var receiver = criteria.Any(p => p.Name == ReceiverName) ? "__" + ReceiverName : ReceiverName; + var criteriaNames = new HashSet(criteria.Select(p => p.Name.TrimStart('@')), StringComparer.Ordinal); + var receiver = GetUniqueName(ReceiverName, criteriaNames); + var invoker = GetUniqueName(InvokerName, criteriaNames); var visibility = type.Visibility == TypeVisibility.Public && criteria.All(p => p.Visibility == TypeVisibility.Public) ? "public" : "internal"; @@ -198,13 +209,13 @@ private static void AppendMethod(IndentedTextWriter writer, ExtensionTypeModel t writer.WriteLine("/// "); writer.WriteLine($"/// Invokes the {Escape(method.MethodDisplay)} {method.Kind} operation of ."); writer.WriteLine("/// "); - writer.WriteLine($"{visibility} static {returnType} {extension.Name}({string.Join(", ", parameters)})"); + writer.WriteLine($"{visibility} static {returnType} {OperationDiscovery.EscapeIdentifier(extension.Name)}({string.Join(", ", parameters)})"); OpenBlock(writer); - writer.WriteLine($"if ({receiver} is {invokerType} __invoker)"); + writer.WriteLine($"if ({receiver} is {invokerType} {invoker})"); writer.Indent++; if (isDelete && !extension.IsAsync) { - writer.WriteLine($"__invoker.{invokerMethod}({invokerArguments});"); + writer.WriteLine($"{invoker}.{invokerMethod}({invokerArguments});"); writer.Indent--; writer.WriteLine("else"); writer.Indent++; @@ -213,13 +224,24 @@ private static void AppendMethod(IndentedTextWriter writer, ExtensionTypeModel t } else { - writer.WriteLine($"{returnKeyword}__invoker.{invokerMethod}({invokerArguments});"); + writer.WriteLine($"{returnKeyword}{invoker}.{invokerMethod}({invokerArguments});"); writer.Indent--; writer.WriteLine($"{returnKeyword}{receiver}.{portalMethod}({fallbackArguments});"); } CloseBlock(writer); } + /// + /// Returns a name for a generated parameter or local that does not + /// collide with any operation parameter name. + /// + private static string GetUniqueName(string name, HashSet usedNames) + { + while (usedNames.Contains(name)) + name = "__" + name; + return name; + } + private static string Escape(string text) => text.Replace("&", "&").Replace("<", "<").Replace(">", ">"); private static DiagnosticInfo Diagnostic(string id, LocationInfo? location, params string[] args) diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/ExtensionTypeModel.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/ExtensionTypeModel.cs index 265e2df6fa..56a0b27da9 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/ExtensionTypeModel.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/ExtensionTypeModel.cs @@ -48,10 +48,16 @@ internal sealed record ExtensionTypeModel public LocationInfo? Location { get; init; } /// - /// Names of instance members of IDataPortal<T> and IChildDataPortal<T>, - /// which would hide extension methods with the same name. + /// Names of instance members of IDataPortal<T>, which would hide + /// root extension methods with the same name. /// - public required EquatableArray HiddenNames { get; init; } + public required EquatableArray RootHiddenNames { get; init; } + + /// + /// Names of instance members of IChildDataPortal<T>, which would hide + /// child extension methods with the same name. + /// + public required EquatableArray ChildHiddenNames { get; init; } } /// diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/IncrementalDataPortalExtensionsGenerator.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/IncrementalDataPortalExtensionsGenerator.cs index 9432045340..6c5755ccb2 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/IncrementalDataPortalExtensionsGenerator.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/IncrementalDataPortalExtensionsGenerator.cs @@ -94,15 +94,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context) hintParts.Add(type.Name); var compilation = context.SemanticModel.Compilation; - var hiddenNames = new SortedSet(StringComparer.Ordinal); - foreach (var portalInterface in new[] { "Csla.IDataPortal`1", "Csla.IChildDataPortal`1" }) - { - var symbol = compilation.GetTypeByMetadataName(portalInterface); - if (symbol is null) - continue; - foreach (var member in symbol.GetMembers()) - hiddenNames.Add(member.Name); - } return new ExtensionTypeModel { @@ -118,8 +109,20 @@ public void Initialize(IncrementalGeneratorInitializationContext context) IsCslaObject = type.AllInterfaces.Any(i => i.ToDisplayString() == "Csla.Core.ICslaObject"), Visibility = OperationDiscovery.GetVisibility(type), Location = LocationInfo.From(type.Locations.FirstOrDefault()), - HiddenNames = new EquatableArray(hiddenNames) + RootHiddenNames = GetMemberNames(compilation, "Csla.IDataPortal`1"), + ChildHiddenNames = GetMemberNames(compilation, "Csla.IChildDataPortal`1") }; } + + private static EquatableArray GetMemberNames(Compilation compilation, string metadataName) + { + var names = new SortedSet(StringComparer.Ordinal); + if (compilation.GetTypeByMetadataName(metadataName) is { } symbol) + { + foreach (var member in symbol.GetMembers()) + names.Add(member.Name); + } + return new EquatableArray(names); + } } } diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsBuilder.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsBuilder.cs index 189aa4de05..27402bf7c2 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsBuilder.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsBuilder.cs @@ -21,6 +21,7 @@ internal static class DataPortalOperationsBuilder { private const string HelperType = "global::Csla.Server.DataPortalOperationHelper"; private const string OperationsLocal = "__operations"; + private const string ExceptionLocal = "__ex"; /// /// Build the generated source for a type. @@ -282,24 +283,49 @@ private static void AppendDispatch(IndentedTextWriter writer, OperationMethodMod var i = criteriaIndex++; string argument; if (byName && parameter.AcceptsNull) + { argument = parameter.IsNullableValueType ? $"({parameter.TypeDisplay})criteria[{i}]" : $"({parameter.TypeDisplay})criteria[{i}]!"; + // An explicit 'in' argument must be a variable, not a cast expression. + if (parameter.RefKindPrefix == "in ") + { + var variable = CriteriaVariable(candidateIndex, i); + writer.WriteLine($"var {variable} = {argument};"); + argument = variable; + } + } else + { argument = CriteriaVariable(candidateIndex, i); + } arguments.Add(ArgumentPrefix(parameter) + argument); } } + // Like reflection-based dispatch, exceptions thrown by the operation + // method are wrapped in CallMethodException. This also keeps an operation + // method throwing DataPortalOperationNotSupportedException from being + // treated as an unmatched operation and invoked again through reflection. var call = $"{OperationsLocal}.{memberName}({string.Join(", ", arguments)})"; + writer.WriteLine("try"); + OpenBlock(writer); writer.WriteLine(method.IsAsync ? $"await {call}.ConfigureAwait(false);" : $"{call};"); + CloseBlock(writer); + writer.WriteLine($"catch (global::System.Exception {ExceptionLocal})"); + OpenBlock(writer); + writer.WriteLine($"throw {HelperType}.CreateCallMethodException(this, \"{method.MethodName.TrimStart('@')}\", {ExceptionLocal});"); + CloseBlock(writer); writer.WriteLine("return;"); CloseBlock(writer); } private static string FormatServiceResolution(OperationParameterModel parameter) { - var serviceType = $"typeof({parameter.PatternTypeDisplay})"; + // Nullable services are registered and resolved as Nullable, as in reflection-based dispatch. + var serviceType = parameter.IsNullableValueType + ? $"typeof({parameter.PatternTypeDisplay}?)" + : $"typeof({parameter.PatternTypeDisplay})"; var resolution = parameter.ServiceKeyExpression is null ? $"{HelperType}.GetService(serviceProvider, {serviceType}, {(parameter.AllowNull ? "true" : "false")})" : $"{HelperType}.GetKeyedService(serviceProvider, {serviceType}, {parameter.ServiceKeyExpression}, {(parameter.AllowNull ? "true" : "false")})"; diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs index fb94c2f90f..2468d6de65 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs @@ -24,6 +24,7 @@ internal static class DataPortalOperationsDiagnostics public const string ExtensionNameHiddenId = "CSLADP005"; public const string InaccessibleParameterTypeId = "CSLADP006"; public const string DuplicateExtensionMethodId = "CSLADP007"; + public const string InvalidExtensionPrefixId = "CSLADP008"; public static readonly DiagnosticDescriptor DuplicateOperationName = new( id: DuplicateOperationNameId, @@ -62,7 +63,7 @@ internal static class DataPortalOperationsDiagnostics title: "Operation parameter type is not accessible", messageFormat: "The data portal extension method for '{0}' cannot be generated because parameter '{1}' has type '{2}', which is not accessible outside its containing type", category: Category, - defaultSeverity: DiagnosticSeverity.Error, + defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true); public static readonly DiagnosticDescriptor DuplicateExtensionMethod = new( @@ -73,6 +74,14 @@ internal static class DataPortalOperationsDiagnostics defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true); + public static readonly DiagnosticDescriptor InvalidExtensionPrefix = new( + id: InvalidExtensionPrefixId, + title: "Invalid DataPortalExtensions prefix", + messageFormat: "Data portal extension methods are not generated for '{0}' because the prefix '{1}' is not a valid C# identifier", + category: Category, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + public static DiagnosticDescriptor GetDescriptor(string id) => id switch { DuplicateOperationNameId => DuplicateOperationName, @@ -81,6 +90,7 @@ internal static class DataPortalOperationsDiagnostics ExtensionNameHiddenId => ExtensionNameHidden, InaccessibleParameterTypeId => InaccessibleParameterType, DuplicateExtensionMethodId => DuplicateExtensionMethod, + InvalidExtensionPrefixId => InvalidExtensionPrefix, _ => throw new ArgumentOutOfRangeException(nameof(id), id, null) }; } diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs index 23ef07560e..f41fccf06b 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs @@ -173,6 +173,10 @@ private static OperationParameterModel BuildParameter(IParameterSymbol parameter } } + // Like reflection-based dispatch, a nullable parameter type makes the service optional. + if (isInjected && (isNullableValueType || (type.IsReferenceType && parameter.NullableAnnotation == NullableAnnotation.Annotated))) + allowNull = true; + return new OperationParameterModel { Name = EscapeIdentifier(parameter.Name), diff --git a/Source/Csla/DataPortalT.cs b/Source/Csla/DataPortalT.cs index a685169993..643d6524ae 100644 --- a/Source/Csla/DataPortalT.cs +++ b/Source/Csla/DataPortalT.cs @@ -600,12 +600,12 @@ public async Task ExecuteAsync(params object?[]? criteria) /// /// Converts the explicit criteria array supplied by generated code into /// the criteria object used by the data portal. A single criteria value - /// that is itself a covariant array (for example string[]) is wrapped so - /// it is not expanded into separate criteria values. + /// that is itself an array of reference types (for example string[] or + /// object[]) is wrapped so it is not expanded into separate criteria values. /// private static object GetOperationCriteria(object?[]? criteria) { - if (criteria is { Length: 1 } && criteria[0] is object[] array && array.GetType() != typeof(object[])) + if (criteria is { Length: 1 } && criteria[0] is object[]) return new Core.MobileList(criteria); return Server.DataPortal.GetCriteriaFromArray(criteria); } diff --git a/Source/Csla/Server/DataPortalOperationHelper.cs b/Source/Csla/Server/DataPortalOperationHelper.cs index cb280d602c..74313fa00a 100644 --- a/Source/Csla/Server/DataPortalOperationHelper.cs +++ b/Source/Csla/Server/DataPortalOperationHelper.cs @@ -43,6 +43,24 @@ public static void ThrowIfAsyncMethodOnSyncClient(object target, bool isSync, IS new NotSupportedException(string.Format(Resources.AsyncMethodOnSyncClientNotAllowed, methodName))); } + /// + /// Wraps an exception thrown by an operation method in a + /// , matching the behavior of + /// reflection-based dispatch. Because the exception is wrapped, an + /// operation method that throws + /// is not mistaken for an unmatched generated dispatch. + /// + /// Business object declaring the method. + /// Name of the operation method. + /// Exception thrown by the operation method. + public static Exception CreateCallMethodException(object target, string methodName, Exception exception) + { + if (target is null) + throw new ArgumentNullException(nameof(target)); + + return new CallMethodException(target.GetType().Name + "." + methodName + " " + Resources.MethodCallFailed, exception); + } + /// /// Resolves a service for an injected operation method parameter. /// diff --git a/Source/Csla/Server/IDataPortalOperationMapping.cs b/Source/Csla/Server/IDataPortalOperationMapping.cs index fb6d53ca4e..07694931f7 100644 --- a/Source/Csla/Server/IDataPortalOperationMapping.cs +++ b/Source/Csla/Server/IDataPortalOperationMapping.cs @@ -23,7 +23,10 @@ public interface IDataPortalOperationMapping /// Service provider for resolving injected dependencies /// A Task representing the async operation /// - /// Thrown when the operation/criteria combination is not handled by the generated code + /// Thrown when the operation/criteria combination is not handled by the generated code. + /// Exceptions thrown by the operation method itself must be wrapped (as + /// does) so they + /// are not mistaken for an unmatched operation, which would invoke it again through reflection. /// Task InvokeOperationAsync(Type operationType, bool isSync, object?[]? criteria, IServiceProvider serviceProvider); } diff --git a/Source/Csla/Server/IDataPortalOperationNamedMapping.cs b/Source/Csla/Server/IDataPortalOperationNamedMapping.cs index 9677c97084..9e90a1cf30 100644 --- a/Source/Csla/Server/IDataPortalOperationNamedMapping.cs +++ b/Source/Csla/Server/IDataPortalOperationNamedMapping.cs @@ -23,7 +23,10 @@ public interface IDataPortalOperationNamedMapping /// Service provider for resolving injected dependencies /// A Task representing the async operation /// - /// Thrown when the operation name is not handled by the generated code + /// Thrown when the operation name is not handled by the generated code. + /// Exceptions thrown by the operation method itself must be wrapped (as + /// does) so they + /// are not mistaken for an unmatched operation, which would invoke it again through reflection. /// Task InvokeNamedOperationAsync(string operationName, bool isSync, object?[]? criteria, IServiceProvider serviceProvider); } diff --git a/Source/tests/Csla.Analyzers.Tests/UseGeneratedDataPortalExtensionAnalyzerTests.cs b/Source/tests/Csla.Analyzers.Tests/UseGeneratedDataPortalExtensionAnalyzerTests.cs index 7fc648703b..5fbd87297a 100644 --- a/Source/tests/Csla.Analyzers.Tests/UseGeneratedDataPortalExtensionAnalyzerTests.cs +++ b/Source/tests/Csla.Analyzers.Tests/UseGeneratedDataPortalExtensionAnalyzerTests.cs @@ -13,19 +13,19 @@ public sealed class UseGeneratedDataPortalExtensionAnalyzerTests public partial class A : BusinessBase { [Create] - private void Create() { } + private void New() { } [Fetch] - private void Fetch(int id) { } + private void GetById(int id) { } [Delete] - private void Delete(int id) { } + private void Remove(int id) { } [FetchChild] - private void FetchChild(int id) { } + private void LoadChild(int id) { } [CreateChild] - private void CreateChild() { } + private void NewChild() { } } [DataPortalExtensions] @@ -33,7 +33,7 @@ private void CreateChild() { } public partial class C : CommandBase { [Execute] - private void Execute(int id) { } + private void Run(int id) { } } [Serializable] @@ -44,7 +44,7 @@ private void Fetch(int id) { } } """; - private static string CreateCode(string body, string header = "") => + private static string CreateCode(string body, string header = "", string additionalTypes = "") => $$""" {{header}} using Csla; @@ -53,6 +53,8 @@ private static string CreateCode(string body, string header = "") => {{BusinessTypes}} + {{additionalTypes}} + public class Consumer { public async Task Run(IDataPortal portal, IChildDataPortal childPortal, @@ -230,6 +232,113 @@ await TestHelpers.RunAnalysisAsync(code diagnostics => Assert.IsTrue(diagnostics[0].GetMessage().Contains("'CreateAsync'"))); } + [TestMethod] + public async Task AnalyzeWhenCriteriaArrayIsPassed() + { + var code = CreateCode( + """ + object[] criteria = [1]; + await portal.FetchAsync(criteria); + """); + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension]); + } + + [TestMethod] + public async Task AnalyzeWhenCriteriaCountDoesNotMatchAnyExtension() + { + var code = CreateCode( + """ + await portal.FetchAsync(1, 2); + await portal.CreateAsync(1); + """); + await TestHelpers.RunAnalysisAsync(code, []); + } + + [TestMethod] + public async Task AnalyzeWhenNoExtensionIsGeneratedForTheOperation() + { + var code = CreateCode( + """ + IDataPortal dPortal = null; + IChildDataPortal dChildPortal = null; + await dPortal.FetchAsync(1); + dPortal.Delete(1); + await dPortal.CreateAsync("a"); + await dPortal.ExecuteAsync(1); + dChildPortal.CreateChild(1); + """, + additionalTypes: + """ + [DataPortalExtensions] + [Serializable] + public partial class D : BusinessBase + { + private class Secret { } + + [NoDataPortalExtension] + [Fetch] + private void GetById(int id) { } + + [Delete] + private void Delete(int id) { } + + [Create] + private void NewWith(Secret secret) { } + + [Create] + private void NewByRef(ref string name) { } + + [FetchChild] + private void LoadChild(int id) { } + } + """); + await TestHelpers.RunAnalysisAsync(code, []); + } + + [TestMethod] + public async Task AnalyzeWhenRootOperationNameIsOnlyAChildPortalMember() + { + var code = CreateCode( + """ + IDataPortal dPortal = null; + await dPortal.CreateAsync(1); + """, + additionalTypes: + """ + [DataPortalExtensions] + [Serializable] + public partial class D : BusinessBase + { + [Create] + private void CreateChild(int id) { } + } + """); + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension]); + } + + [TestMethod] + public async Task AnalyzeWhenPrefixIsInvalid() + { + var code = CreateCode( + """ + IDataPortal dPortal = null; + await dPortal.FetchAsync(1); + """, + additionalTypes: + """ + [DataPortalExtensions(Prefix = "My-")] + [Serializable] + public partial class D : BusinessBase + { + [Fetch] + private void GetById(int id) { } + } + """); + await TestHelpers.RunAnalysisAsync(code, []); + } + [TestMethod] public async Task AnalyzeWhenCodeIsGenerated() { diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/DataPortalExtensionsGeneratorTests.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/DataPortalExtensionsGeneratorTests.cs index ee15d07407..145356e3bb 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/DataPortalExtensionsGeneratorTests.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/DataPortalExtensionsGeneratorTests.cs @@ -139,6 +139,69 @@ private void GetByCode(string code, int version) { } await VerifyWithDiagnostics(source); } + [TestMethod("Hidden names are checked against the receiving portal interface only")] + public async Task ReceiverSpecificHiddenNames() + { + var source = """ + using Csla; + + namespace TestApp + { + [DataPortalExtensions] + public partial class PersonEdit : BusinessBase + { + [Create] + private void CreateChild(int id) { } + + [FetchChild] + private void Fetch(int id) { } + } + } + """; + + await Verify(source); + } + + [TestMethod("Keyword method names and parameters colliding with generated names")] + public async Task KeywordAndCollidingNames() + { + var source = """ + using Csla; + + namespace TestApp + { + [DataPortalExtensions] + public partial class PersonEdit : BusinessBase + { + [Fetch] + private void @class(int portal, int __portal, int __invoker) { } + } + } + """; + + await Verify(source); + } + + [TestMethod("A prefix that is not a valid identifier is reported")] + public async Task InvalidPrefix() + { + var source = """ + using Csla; + + namespace TestApp + { + [DataPortalExtensions(Prefix = "My-")] + public partial class PersonEdit : BusinessBase + { + [Fetch] + private void GetById(int id) { } + } + } + """; + + await VerifyWithDiagnostics(source); + } + [TestMethod("NoDataPortalExtension excludes a method")] public async Task NoDataPortalExtension() { diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidPrefix.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidPrefix.verified.txt new file mode 100644 index 0000000000..984943d3fd --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidPrefix.verified.txt @@ -0,0 +1,18 @@ +{ + Diagnostics: [ + { + Location: Source0.cs: (5,23)-(5,33), + Message: Data portal extension methods are not generated for 'PersonEdit' because the prefix 'My-' is not a valid C# identifier, + Severity: Warning, + WarningLevel: 1, + Descriptor: { + Id: CSLADP008, + Title: Invalid DataPortalExtensions prefix, + MessageFormat: Data portal extension methods are not generated for '{0}' because the prefix '{1}' is not a valid C# identifier, + Category: Csla.DataPortal, + DefaultSeverity: Warning, + IsEnabledByDefault: true + } + } + ] +} \ No newline at end of file diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.KeywordAndCollidingNames#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.KeywordAndCollidingNames#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs new file mode 100644 index 0000000000..d1a25ce04c --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.KeywordAndCollidingNames#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs @@ -0,0 +1,32 @@ +//HintName: TestApp.PersonEdit.DataPortalExtensions.g.cs +// +#nullable enable + +namespace TestApp +{ + /// + /// Strongly typed data portal extension methods for . + /// + public static partial class PersonEditDataPortalExtensions + { + /// + /// Invokes the class(int, int, int) Fetch operation of . + /// + public static global::System.Threading.Tasks.Task classAsync(this global::Csla.IDataPortal ____portal, int portal, int __portal, int __invoker) + { + if (____portal is global::Csla.IDataPortalOperationInvoker ____invoker) + return ____invoker.FetchByOperationAsync("Fetch__Int32_Int32_Int32", false, new object?[] { portal, __portal, __invoker }); + return ____portal.FetchAsync(new object?[] { portal, __portal, __invoker }); + } + + /// + /// Invokes the class(int, int, int) Fetch operation of . + /// + public static global::TestApp.PersonEdit @class(this global::Csla.IDataPortal ____portal, int portal, int __portal, int __invoker) + { + if (____portal is global::Csla.IDataPortalOperationInvoker ____invoker) + return ____invoker.FetchByOperation("Fetch__Int32_Int32_Int32", false, new object?[] { portal, __portal, __invoker }); + return ____portal.Fetch(new object?[] { portal, __portal, __invoker }); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ParameterAccessibility.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ParameterAccessibility.verified.txt index 3510e712ea..5a1b813a37 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ParameterAccessibility.verified.txt +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ParameterAccessibility.verified.txt @@ -3,13 +3,14 @@ { Location: Source0.cs: (15,17)-(15,29), Message: The data portal extension method for 'GetByPrivate(PrivateCriteria)' cannot be generated because parameter 'criteria' has type 'global::TestApp.PersonEdit.PrivateCriteria', which is not accessible outside its containing type, - Severity: Error, + Severity: Warning, + WarningLevel: 1, Descriptor: { Id: CSLADP006, Title: Operation parameter type is not accessible, MessageFormat: The data portal extension method for '{0}' cannot be generated because parameter '{1}' has type '{2}', which is not accessible outside its containing type, Category: Csla.DataPortal, - DefaultSeverity: Error, + DefaultSeverity: Warning, IsEnabledByDefault: true } } diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ReceiverSpecificHiddenNames#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ReceiverSpecificHiddenNames#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs new file mode 100644 index 0000000000..5394a5cbd4 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ReceiverSpecificHiddenNames#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs @@ -0,0 +1,52 @@ +//HintName: TestApp.PersonEdit.DataPortalExtensions.g.cs +// +#nullable enable + +namespace TestApp +{ + /// + /// Strongly typed data portal extension methods for . + /// + public static partial class PersonEditDataPortalExtensions + { + /// + /// Invokes the CreateChild(int) Create operation of . + /// + public static global::System.Threading.Tasks.Task CreateChildAsync(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.CreateByOperationAsync("Create__Int32", false, new object?[] { id }); + return portal.CreateAsync(new object?[] { id }); + } + + /// + /// Invokes the CreateChild(int) Create operation of . + /// + public static global::TestApp.PersonEdit CreateChild(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.CreateByOperation("Create__Int32", false, new object?[] { id }); + return portal.Create(new object?[] { id }); + } + + /// + /// Invokes the Fetch(int) FetchChild operation of . + /// + public static global::System.Threading.Tasks.Task FetchAsync(this global::Csla.IChildDataPortal portal, int id) + { + if (portal is global::Csla.IChildDataPortalOperationInvoker __invoker) + return __invoker.FetchChildByOperationAsync("FetchChild__Int32", new object?[] { id }); + return portal.FetchChildAsync(new object?[] { id }); + } + + /// + /// Invokes the Fetch(int) FetchChild operation of . + /// + public static global::TestApp.PersonEdit Fetch(this global::Csla.IChildDataPortal portal, int id) + { + if (portal is global::Csla.IChildDataPortalOperationInvoker __invoker) + return __invoker.FetchChildByOperation("FetchChild__Int32", new object?[] { id }); + return portal.FetchChild(new object?[] { id }); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsGeneratorTests.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsGeneratorTests.cs index 7b718eb975..74f502ff14 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsGeneratorTests.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsGeneratorTests.cs @@ -583,6 +583,30 @@ private void Create(int @class, string @event = "x") { } await TestHelperVerify(source); } + [TestMethod("In criteria parameters and nullable inject parameters")] + public async Task InCriteriaAndNullableInject() + { + var source = """ + using Csla; + + namespace TestApp + { + public interface IDal { } + + public partial class PersonEdit : Csla.BusinessBase + { + [Fetch] + private void Fetch(in string? name, in int id) { } + + [Create] + private void Create([Inject] int? count, [Inject] IDal? dal) { } + } + } + """; + + await TestHelperVerify(source); + } + [TestMethod("Non-partial class generates nothing")] public async Task NonPartialClass() { diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index c9adc2bcae..027119f4c2 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -31,17 +31,38 @@ internal interface IDataPortalOperations { if (criteria is { Length: 1 } && criteria[0] is object __c0p0 && __c0p0.GetType() == typeof(object)) { - __operations.Fetch__Object(__c0p0); + try + { + __operations.Fetch__Object(__c0p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } if (criteria is { Length: 1 } && criteria[0] is global::TestApp.Criteria __c1p0 && __c1p0.GetType() == typeof(global::TestApp.Criteria)) { - __operations.Fetch__Criteria(__c1p0); + try + { + __operations.Fetch__Criteria(__c1p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } if (criteria is { Length: 1 } && criteria[0] is int __c2p0) { - __operations.Fetch__Int32(__c2p0); + try + { + __operations.Fetch__Int32(__c2p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } } @@ -57,21 +78,42 @@ internal interface IDataPortalOperations case "Fetch__Object": if (criteria is { Length: 1 } && (criteria[0] is null || criteria[0] is object)) { - __operations.Fetch__Object((object)criteria[0]!); + try + { + __operations.Fetch__Object((object)criteria[0]!); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; case "Fetch__Criteria": if (criteria is { Length: 1 } && (criteria[0] is null || criteria[0] is global::TestApp.Criteria)) { - __operations.Fetch__Criteria((global::TestApp.Criteria)criteria[0]!); + try + { + __operations.Fetch__Criteria((global::TestApp.Criteria)criteria[0]!); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; case "Fetch__Int32": if (criteria is { Length: 1 } && criteria[0] is int __c2p0) { - __operations.Fetch__Int32(__c2p0); + try + { + __operations.Fetch__Int32(__c2p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ArrayNullableGenericCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ArrayNullableGenericCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 947abc325c..5c2dee87fd 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ArrayNullableGenericCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ArrayNullableGenericCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -35,27 +35,62 @@ internal interface IDataPortalOperations { if (criteria is { Length: 1 } && criteria[0] is int[] __c0p0 && __c0p0.GetType() == typeof(int[])) { - __operations.Fetch__Int32Array(__c0p0); + try + { + __operations.Fetch__Int32Array(__c0p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } if (criteria is { Length: 2 } && criteria[0] is int __c1p0 && criteria[1] is string __c1p1) { - __operations.Fetch__Nullable_1_Int32_String(__c1p0, __c1p1); + try + { + __operations.Fetch__Nullable_1_Int32_String(__c1p0, __c1p1); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } if (criteria is { Length: 1 } && criteria[0] is global::System.Collections.Generic.List __c2p0 && __c2p0.GetType() == typeof(global::System.Collections.Generic.List)) { - __operations.Fetch__List_1_Int32(__c2p0); + try + { + __operations.Fetch__List_1_Int32(__c2p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } if (criteria is { Length: 1 } && criteria[0] is global::System.ValueTuple __c3p0) { - __operations.Fetch__ValueTuple_2_Int32_String(__c3p0); + try + { + __operations.Fetch__ValueTuple_2_Int32_String(__c3p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } if (criteria is { Length: 1 } && criteria[0] is global::System.Collections.Generic.Dictionary>[] __c4p0 && __c4p0.GetType() == typeof(global::System.Collections.Generic.Dictionary>[])) { - __operations.Fetch__Dictionary_2_String_List_1_Nullable_1_Int32Array(__c4p0); + try + { + __operations.Fetch__Dictionary_2_String_List_1_Nullable_1_Int32Array(__c4p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } } @@ -71,35 +106,70 @@ internal interface IDataPortalOperations case "Fetch__Int32Array": if (criteria is { Length: 1 } && (criteria[0] is null || criteria[0] is int[])) { - __operations.Fetch__Int32Array((int[])criteria[0]!); + try + { + __operations.Fetch__Int32Array((int[])criteria[0]!); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; case "Fetch__Nullable_1_Int32_String": if (criteria is { Length: 2 } && (criteria[0] is null || criteria[0] is int) && (criteria[1] is null || criteria[1] is string)) { - __operations.Fetch__Nullable_1_Int32_String((int?)criteria[0], (string?)criteria[1]!); + try + { + __operations.Fetch__Nullable_1_Int32_String((int?)criteria[0], (string?)criteria[1]!); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; case "Fetch__List_1_Int32": if (criteria is { Length: 1 } && (criteria[0] is null || criteria[0] is global::System.Collections.Generic.List)) { - __operations.Fetch__List_1_Int32((global::System.Collections.Generic.List)criteria[0]!); + try + { + __operations.Fetch__List_1_Int32((global::System.Collections.Generic.List)criteria[0]!); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; case "Fetch__ValueTuple_2_Int32_String": if (criteria is { Length: 1 } && criteria[0] is global::System.ValueTuple __c3p0) { - __operations.Fetch__ValueTuple_2_Int32_String(__c3p0); + try + { + __operations.Fetch__ValueTuple_2_Int32_String(__c3p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; case "Fetch__Dictionary_2_String_List_1_Nullable_1_Int32Array": if (criteria is { Length: 1 } && (criteria[0] is null || criteria[0] is global::System.Collections.Generic.Dictionary>[])) { - __operations.Fetch__Dictionary_2_String_List_1_Nullable_1_Int32Array((global::System.Collections.Generic.Dictionary>[])criteria[0]!); + try + { + __operations.Fetch__Dictionary_2_String_List_1_Nullable_1_Int32Array((global::System.Collections.Generic.Dictionary>[])criteria[0]!); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AsyncMethod#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AsyncMethod#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 7289d37ab6..292debe11f 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AsyncMethod#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AsyncMethod#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -28,7 +28,14 @@ internal interface IDataPortalOperations if (criteria is { Length: 1 } && criteria[0] is int __c0p0) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); - await __operations.Fetch__Int32(__c0p0).ConfigureAwait(false); + try + { + await __operations.Fetch__Int32(__c0p0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } } @@ -45,7 +52,14 @@ internal interface IDataPortalOperations if (criteria is { Length: 1 } && criteria[0] is int __c0p0) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); - await __operations.Fetch__Int32(__c0p0).ConfigureAwait(false); + try + { + await __operations.Fetch__Int32(__c0p0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs index b7081044db..ea4de6bb0d 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs @@ -35,7 +35,14 @@ internal interface IDataPortalOperations { if (criteria is null or { Length: 0 }) { - __operations.CreateChild(); + try + { + __operations.CreateChild(); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "CreateChild", __ex); + } return; } } @@ -43,7 +50,14 @@ internal interface IDataPortalOperations { if (criteria is { Length: 1 } && criteria[0] is int __c1p0) { - __operations.FetchChild__Int32(__c1p0); + try + { + __operations.FetchChild__Int32(__c1p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "FetchChild", __ex); + } return; } } @@ -53,7 +67,14 @@ internal interface IDataPortalOperations { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "InsertChild"); var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - await __operations.InsertChild(__c2i0).ConfigureAwait(false); + try + { + await __operations.InsertChild(__c2i0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "InsertChild", __ex); + } return; } } @@ -63,7 +84,14 @@ internal interface IDataPortalOperations { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "UpdateChild"); var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - await __operations.UpdateChild(__c3i0).ConfigureAwait(false); + try + { + await __operations.UpdateChild(__c3i0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "UpdateChild", __ex); + } return; } } @@ -73,7 +101,14 @@ internal interface IDataPortalOperations { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "DeleteSelfChild"); var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - await __operations.DeleteSelfChild(__c4i0).ConfigureAwait(false); + try + { + await __operations.DeleteSelfChild(__c4i0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "DeleteSelfChild", __ex); + } return; } } @@ -89,14 +124,28 @@ internal interface IDataPortalOperations case "CreateChild": if (criteria is null or { Length: 0 }) { - __operations.CreateChild(); + try + { + __operations.CreateChild(); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "CreateChild", __ex); + } return; } break; case "FetchChild__Int32": if (criteria is { Length: 1 } && criteria[0] is int __c1p0) { - __operations.FetchChild__Int32(__c1p0); + try + { + __operations.FetchChild__Int32(__c1p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "FetchChild", __ex); + } return; } break; @@ -105,7 +154,14 @@ internal interface IDataPortalOperations { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "InsertChild"); var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - await __operations.InsertChild(__c2i0).ConfigureAwait(false); + try + { + await __operations.InsertChild(__c2i0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "InsertChild", __ex); + } return; } break; @@ -114,7 +170,14 @@ internal interface IDataPortalOperations { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "UpdateChild"); var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - await __operations.UpdateChild(__c3i0).ConfigureAwait(false); + try + { + await __operations.UpdateChild(__c3i0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "UpdateChild", __ex); + } return; } break; @@ -123,7 +186,14 @@ internal interface IDataPortalOperations { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "DeleteSelfChild"); var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - await __operations.DeleteSelfChild(__c4i0).ConfigureAwait(false); + try + { + await __operations.DeleteSelfChild(__c4i0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "DeleteSelfChild", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index a2bf2d695e..5721e68e15 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -35,7 +35,14 @@ internal interface IDataPortalOperations { if (criteria is null or { Length: 0 }) { - __operations.Create(); + try + { + __operations.Create(); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } return; } } @@ -43,7 +50,14 @@ internal interface IDataPortalOperations { if (criteria is { Length: 1 } && criteria[0] is int __c1p0) { - __operations.Fetch__Int32(__c1p0); + try + { + __operations.Fetch__Int32(__c1p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } } @@ -53,7 +67,14 @@ internal interface IDataPortalOperations { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Insert"); var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - await __operations.Insert(__c2i0).ConfigureAwait(false); + try + { + await __operations.Insert(__c2i0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Insert", __ex); + } return; } } @@ -63,7 +84,14 @@ internal interface IDataPortalOperations { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Update"); var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - await __operations.Update(__c3i0).ConfigureAwait(false); + try + { + await __operations.Update(__c3i0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Update", __ex); + } return; } } @@ -73,7 +101,14 @@ internal interface IDataPortalOperations { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "DeleteSelf"); var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - await __operations.DeleteSelf(__c4i0).ConfigureAwait(false); + try + { + await __operations.DeleteSelf(__c4i0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "DeleteSelf", __ex); + } return; } } @@ -89,14 +124,28 @@ internal interface IDataPortalOperations case "Create": if (criteria is null or { Length: 0 }) { - __operations.Create(); + try + { + __operations.Create(); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } return; } break; case "Fetch__Int32": if (criteria is { Length: 1 } && criteria[0] is int __c1p0) { - __operations.Fetch__Int32(__c1p0); + try + { + __operations.Fetch__Int32(__c1p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; @@ -105,7 +154,14 @@ internal interface IDataPortalOperations { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Insert"); var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - await __operations.Insert(__c2i0).ConfigureAwait(false); + try + { + await __operations.Insert(__c2i0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Insert", __ex); + } return; } break; @@ -114,7 +170,14 @@ internal interface IDataPortalOperations { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Update"); var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - await __operations.Update(__c3i0).ConfigureAwait(false); + try + { + await __operations.Update(__c3i0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Update", __ex); + } return; } break; @@ -123,7 +186,14 @@ internal interface IDataPortalOperations { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "DeleteSelf"); var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - await __operations.DeleteSelf(__c4i0).ConfigureAwait(false); + try + { + await __operations.DeleteSelf(__c4i0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "DeleteSelf", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DeleteWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DeleteWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 51fed68d3b..d98d640ff5 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DeleteWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DeleteWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -27,7 +27,14 @@ internal interface IDataPortalOperations { if (criteria is { Length: 1 } && criteria[0] is int __c0p0) { - __operations.Delete__Int32(__c0p0); + try + { + __operations.Delete__Int32(__c0p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Delete", __ex); + } return; } } @@ -43,7 +50,14 @@ internal interface IDataPortalOperations case "Delete__Int32": if (criteria is { Length: 1 } && criteria[0] is int __c0p0) { - __operations.Delete__Int32(__c0p0); + try + { + __operations.Delete__Int32(__c0p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Delete", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DerivedFromBaseWithOperations#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DerivedFromBaseWithOperations#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index d4981bac36..b1a9ebee86 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DerivedFromBaseWithOperations#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DerivedFromBaseWithOperations#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -27,7 +27,14 @@ partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, glo { if (criteria is { Length: 1 } && criteria[0] is string __c0p0) { - __operations.Fetch__String(__c0p0); + try + { + __operations.Fetch__String(__c0p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } } @@ -43,7 +50,14 @@ partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, glo case "Fetch__String": if (criteria is { Length: 1 } && (criteria[0] is null || criteria[0] is string)) { - __operations.Fetch__String((string)criteria[0]!); + try + { + __operations.Fetch__String((string)criteria[0]!); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 3afd11b461..89a45ec7b0 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -32,7 +32,14 @@ internal interface IDataPortalOperations if (criteria is { Length: 1 } && criteria[0] is int __c0p0) { var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - __operations.Fetch__Int32(__c0p0, __c0i0); + try + { + __operations.Fetch__Int32(__c0p0, __c0i0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } } @@ -49,7 +56,14 @@ internal interface IDataPortalOperations if (criteria is { Length: 1 } && criteria[0] is int __c0p0) { var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - __operations.Fetch__Int32(__c0p0, __c0i0); + try + { + __operations.Fetch__Int32(__c0p0, __c0i0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ExecuteCommand#TestApp.MyCommand.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ExecuteCommand#TestApp.MyCommand.DataPortalOperations.g.verified.cs index 88ca1c8d6f..40d9d287d8 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ExecuteCommand#TestApp.MyCommand.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ExecuteCommand#TestApp.MyCommand.DataPortalOperations.g.verified.cs @@ -27,7 +27,14 @@ internal interface IDataPortalOperations { if (criteria is null or { Length: 0 }) { - __operations.Execute(); + try + { + __operations.Execute(); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Execute", __ex); + } return; } } @@ -43,7 +50,14 @@ internal interface IDataPortalOperations case "Execute": if (criteria is null or { Length: 0 }) { - __operations.Execute(); + try + { + __operations.Execute(); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Execute", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ExistingNamedMapping#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ExistingNamedMapping#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 9948cdee01..30791f457c 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ExistingNamedMapping#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ExistingNamedMapping#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -27,7 +27,14 @@ internal interface IDataPortalOperations { if (criteria is { Length: 1 } && criteria[0] is int __c0p0) { - __operations.Fetch__Int32(__c0p0); + try + { + __operations.Fetch__Int32(__c0p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } } diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.FileScopedNamespace#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.FileScopedNamespace#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 8656190c6c..fcf69747ae 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.FileScopedNamespace#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.FileScopedNamespace#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -29,7 +29,14 @@ internal interface IDataPortalOperations { if (criteria is null or { Length: 0 }) { - __operations.Create(); + try + { + __operations.Create(); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } return; } } @@ -37,7 +44,14 @@ internal interface IDataPortalOperations { if (criteria is { Length: 1 } && criteria[0] is int __c1p0) { - __operations.Fetch__Int32(__c1p0); + try + { + __operations.Fetch__Int32(__c1p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } } @@ -53,14 +67,28 @@ internal interface IDataPortalOperations case "Create": if (criteria is null or { Length: 0 }) { - __operations.Create(); + try + { + __operations.Create(); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } return; } break; case "Fetch__Int32": if (criteria is { Length: 1 } && criteria[0] is int __c1p0) { - __operations.Fetch__Int32(__c1p0); + try + { + __operations.Fetch__Int32(__c1p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.GenericBusinessObject#TestApp.Lookup_1.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.GenericBusinessObject#TestApp.Lookup_1.DataPortalOperations.g.verified.cs index ccbf49514d..19ebfa717c 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.GenericBusinessObject#TestApp.Lookup_1.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.GenericBusinessObject#TestApp.Lookup_1.DataPortalOperations.g.verified.cs @@ -29,12 +29,26 @@ internal interface IDataPortalOperations { if (criteria is { Length: 1 } && criteria[0] is TKey __c0p0 && __c0p0.GetType() == typeof(TKey)) { - __operations.Fetch__TKey(__c0p0); + try + { + __operations.Fetch__TKey(__c0p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } if (criteria is { Length: 1 } && criteria[0] is int __c1p0) { - __operations.Fetch__Int32(__c1p0); + try + { + __operations.Fetch__Int32(__c1p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } } @@ -50,7 +64,14 @@ internal interface IDataPortalOperations case "Fetch__Int32": if (criteria is { Length: 1 } && criteria[0] is int __c0p0) { - __operations.Fetch__Int32(__c0p0); + try + { + __operations.Fetch__Int32(__c0p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InCriteriaAndNullableInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InCriteriaAndNullableInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..a46002eed3 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InCriteriaAndNullableInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,104 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Fetch__String_Int32(in string? name, in int id); + void Create(int? count, global::TestApp.IDal? dal); + } + + void IDataPortalOperations.Fetch__String_Int32(in string? name, in int id) => Fetch(in name, in id); + void IDataPortalOperations.Create(int? count, global::TestApp.IDal? dal) => Create(count, dal); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.CreateAttribute)) + { + if (criteria is null or { Length: 0 }) + { + var __c0i0 = (int?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(int?), true)); + var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), true)); + try + { + __operations.Create(__c0i0, __c0i1); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } + return; + } + } + else if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 2 } && criteria[0] is string __c1p0 && criteria[1] is int __c1p1) + { + try + { + __operations.Fetch__String_Int32(in __c1p0, in __c1p1); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Fetch__String_Int32": + if (criteria is { Length: 2 } && (criteria[0] is null || criteria[0] is string) && criteria[1] is int __c0p1) + { + var __c0p0 = (string?)criteria[0]!; + try + { + __operations.Fetch__String_Int32(in __c0p0, in __c0p1); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } + return; + } + break; + case "Create": + if (criteria is null or { Length: 0 }) + { + var __c1i0 = (int?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(int?), true)); + var __c1i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), true)); + try + { + __operations.Create(__c1i0, __c1i1); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InjectBeforeCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InjectBeforeCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 455d714574..7fc847e724 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InjectBeforeCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InjectBeforeCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -29,7 +29,14 @@ internal interface IDataPortalOperations { var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), true)); - __operations.Fetch__Int32_String(__c0i0, __c0p0, __c0i1, __c0p1); + try + { + __operations.Fetch__Int32_String(__c0i0, __c0p0, __c0i1, __c0p1); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } } @@ -47,7 +54,14 @@ internal interface IDataPortalOperations { var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), true)); - __operations.Fetch__Int32_String(__c0i0, __c0p0, __c0i1, (string)criteria[1]!); + try + { + __operations.Fetch__Int32_String(__c0i0, __c0p0, __c0i1, (string)criteria[1]!); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.KeyedInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.KeyedInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 945e667f5d..d11894ae25 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.KeyedInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.KeyedInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -29,7 +29,14 @@ internal interface IDataPortalOperations { var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), "primary", false))!; var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), global::TestApp.DalKind.Secondary, true)); - __operations.Fetch__Int32(__c0p0, __c0i0, __c0i1); + try + { + __operations.Fetch__Int32(__c0p0, __c0i0, __c0i1); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } } @@ -47,7 +54,14 @@ internal interface IDataPortalOperations { var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), "primary", false))!; var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), global::TestApp.DalKind.Secondary, true)); - __operations.Fetch__Int32(__c0p0, __c0i0, __c0i1); + try + { + __operations.Fetch__Int32(__c0p0, __c0i0, __c0i1); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.MultipleOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.MultipleOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 14cdc00634..f8c7a1e844 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.MultipleOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.MultipleOverloads#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -29,12 +29,26 @@ internal interface IDataPortalOperations { if (criteria is { Length: 1 } && criteria[0] is string __c0p0) { - __operations.Fetch__String(__c0p0); + try + { + __operations.Fetch__String(__c0p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } if (criteria is { Length: 1 } && criteria[0] is int __c1p0) { - __operations.Fetch__Int32(__c1p0); + try + { + __operations.Fetch__Int32(__c1p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } } @@ -50,14 +64,28 @@ internal interface IDataPortalOperations case "Fetch__String": if (criteria is { Length: 1 } && (criteria[0] is null || criteria[0] is string)) { - __operations.Fetch__String((string)criteria[0]!); + try + { + __operations.Fetch__String((string)criteria[0]!); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; case "Fetch__Int32": if (criteria is { Length: 1 } && criteria[0] is int __c1p0) { - __operations.Fetch__Int32(__c1p0); + try + { + __operations.Fetch__Int32(__c1p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.NestedClass#TestApp.Outer.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.NestedClass#TestApp.Outer.PersonEdit.DataPortalOperations.g.verified.cs index 9fd380ce08..fe4459a3d8 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.NestedClass#TestApp.Outer.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.NestedClass#TestApp.Outer.PersonEdit.DataPortalOperations.g.verified.cs @@ -29,7 +29,14 @@ internal interface IDataPortalOperations { if (criteria is { Length: 1 } && criteria[0] is int __c0p0) { - __operations.Fetch__Int32(__c0p0); + try + { + __operations.Fetch__Int32(__c0p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } } @@ -45,7 +52,14 @@ internal interface IDataPortalOperations case "Fetch__Int32": if (criteria is { Length: 1 } && criteria[0] is int __c0p0) { - __operations.Fetch__Int32(__c0p0); + try + { + __operations.Fetch__Int32(__c0p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index e1231531d6..b84a6d0671 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -29,7 +29,14 @@ internal interface IDataPortalOperations { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); var __c0i0 = (global::TestApp.ILogger?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.ILogger), true)); - await __operations.Fetch__Int32(__c0p0, __c0i0).ConfigureAwait(false); + try + { + await __operations.Fetch__Int32(__c0p0, __c0i0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } } @@ -47,7 +54,14 @@ internal interface IDataPortalOperations { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); var __c0i0 = (global::TestApp.ILogger?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.ILogger), true)); - await __operations.Fetch__Int32(__c0p0, __c0i0).ConfigureAwait(false); + try + { + await __operations.Fetch__Int32(__c0p0, __c0i0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 893bd2db41..d7f044136d 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -29,7 +29,14 @@ internal interface IDataPortalOperations { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Insert"); var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - await __operations.Insert(__c0i0).ConfigureAwait(false); + try + { + await __operations.Insert(__c0i0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Insert", __ex); + } return; } } @@ -47,7 +54,14 @@ internal interface IDataPortalOperations { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Insert"); var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - await __operations.Insert(__c0i0).ConfigureAwait(false); + try + { + await __operations.Insert(__c0i0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Insert", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ParamsArrayAndKeywordNames#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ParamsArrayAndKeywordNames#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 7d0d5497d3..3640bcbb8c 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ParamsArrayAndKeywordNames#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ParamsArrayAndKeywordNames#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -29,7 +29,14 @@ internal interface IDataPortalOperations { if (criteria is { Length: 2 } && criteria[0] is int __c0p0 && criteria[1] is string __c0p1) { - __operations.Create__Int32_String(__c0p0, __c0p1); + try + { + __operations.Create__Int32_String(__c0p0, __c0p1); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } return; } } @@ -45,7 +52,14 @@ internal interface IDataPortalOperations case "Create__Int32_String": if (criteria is { Length: 2 } && criteria[0] is int __c0p0 && (criteria[1] is null || criteria[1] is string)) { - __operations.Create__Int32_String(__c0p0, (string)criteria[1]!); + try + { + __operations.Create__Int32_String(__c0p0, (string)criteria[1]!); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.PartialClassAcrossFiles#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.PartialClassAcrossFiles#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 8656190c6c..fcf69747ae 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.PartialClassAcrossFiles#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.PartialClassAcrossFiles#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -29,7 +29,14 @@ internal interface IDataPortalOperations { if (criteria is null or { Length: 0 }) { - __operations.Create(); + try + { + __operations.Create(); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } return; } } @@ -37,7 +44,14 @@ internal interface IDataPortalOperations { if (criteria is { Length: 1 } && criteria[0] is int __c1p0) { - __operations.Fetch__Int32(__c1p0); + try + { + __operations.Fetch__Int32(__c1p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } } @@ -53,14 +67,28 @@ internal interface IDataPortalOperations case "Create": if (criteria is null or { Length: 0 }) { - __operations.Create(); + try + { + __operations.Create(); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } return; } break; case "Fetch__Int32": if (criteria is { Length: 1 } && criteria[0] is int __c1p0) { - __operations.Fetch__Int32(__c1p0); + try + { + __operations.Fetch__Int32(__c1p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.RootAndChildAttributes#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.RootAndChildAttributes#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index aaefce3bd0..5b03d2a0df 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.RootAndChildAttributes#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.RootAndChildAttributes#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -29,7 +29,14 @@ internal interface IDataPortalOperations { if (criteria is null or { Length: 0 }) { - __operations.Create(); + try + { + __operations.Create(); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } return; } } @@ -37,7 +44,14 @@ internal interface IDataPortalOperations { if (criteria is null or { Length: 0 }) { - __operations.CreateChild(); + try + { + __operations.CreateChild(); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } return; } } @@ -53,14 +67,28 @@ internal interface IDataPortalOperations case "Create": if (criteria is null or { Length: 0 }) { - __operations.Create(); + try + { + __operations.Create(); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } return; } break; case "CreateChild": if (criteria is null or { Length: 0 }) { - __operations.CreateChild(); + try + { + __operations.CreateChild(); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.RunLocalAndValueTask#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.RunLocalAndValueTask#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 0b2bd25d34..47b41957bd 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.RunLocalAndValueTask#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.RunLocalAndValueTask#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -31,7 +31,14 @@ internal interface IDataPortalOperations { if (criteria is null or { Length: 0 }) { - __operations.Create(); + try + { + __operations.Create(); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } return; } } @@ -40,13 +47,27 @@ internal interface IDataPortalOperations if (criteria is { Length: 1 } && criteria[0] is int __c1p0) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); - await __operations.Fetch__Int32(__c1p0).ConfigureAwait(false); + try + { + await __operations.Fetch__Int32(__c1p0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } if (criteria is { Length: 1 } && criteria[0] is string __c2p0) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); - await __operations.Fetch__String(__c2p0).ConfigureAwait(false); + try + { + await __operations.Fetch__String(__c2p0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } } @@ -62,7 +83,14 @@ internal interface IDataPortalOperations case "Create": if (criteria is null or { Length: 0 }) { - __operations.Create(); + try + { + __operations.Create(); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } return; } break; @@ -70,7 +98,14 @@ internal interface IDataPortalOperations if (criteria is { Length: 1 } && criteria[0] is int __c1p0) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); - await __operations.Fetch__Int32(__c1p0).ConfigureAwait(false); + try + { + await __operations.Fetch__Int32(__c1p0).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; @@ -78,7 +113,14 @@ internal interface IDataPortalOperations if (criteria is { Length: 1 } && (criteria[0] is null || criteria[0] is string)) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); - await __operations.Fetch__String((string)criteria[0]!).ConfigureAwait(false); + try + { + await __operations.Fetch__String((string)criteria[0]!).ConfigureAwait(false); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.SingleCreateNoParams#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.SingleCreateNoParams#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 78f5b02452..b534c5a5a4 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.SingleCreateNoParams#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.SingleCreateNoParams#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -27,7 +27,14 @@ internal interface IDataPortalOperations { if (criteria is null or { Length: 0 }) { - __operations.Create(); + try + { + __operations.Create(); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } return; } } @@ -43,7 +50,14 @@ internal interface IDataPortalOperations case "Create": if (criteria is null or { Length: 0 }) { - __operations.Create(); + try + { + __operations.Create(); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } return; } break; diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.SingleFetchWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.SingleFetchWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index f3fc9a663b..fc9b8a3a20 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.SingleFetchWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.SingleFetchWithCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -27,7 +27,14 @@ internal interface IDataPortalOperations { if (criteria is { Length: 1 } && criteria[0] is int __c0p0) { - __operations.Fetch__Int32(__c0p0); + try + { + __operations.Fetch__Int32(__c0p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } } @@ -43,7 +50,14 @@ internal interface IDataPortalOperations case "Fetch__Int32": if (criteria is { Length: 1 } && criteria[0] is int __c0p0) { - __operations.Fetch__Int32(__c0p0); + try + { + __operations.Fetch__Int32(__c0p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } return; } break; diff --git a/Source/tests/csla.netcore.test/DataPortal/DataPortalExtensionsTests.cs b/Source/tests/csla.netcore.test/DataPortal/DataPortalExtensionsTests.cs index fd913fe888..3823c251b7 100644 --- a/Source/tests/csla.netcore.test/DataPortal/DataPortalExtensionsTests.cs +++ b/Source/tests/csla.netcore.test/DataPortal/DataPortalExtensionsTests.cs @@ -10,6 +10,7 @@ using Csla.Channels.Local; using Csla.Core; using Csla.DataPortalClient; +using Csla.Reflection; using Csla.Server; using Csla.Testing; using FluentAssertions; @@ -130,6 +131,58 @@ public void Fetch_Sync_AsyncOperationMethod_ThrowsLikeReflectionDispatch() reflectionException.GetBaseException().Should().BeOfType(); } + [TestMethod] + public async Task FetchAsync_OperationThrows_WrapsExceptionLikeReflectionDispatch() + { + var generated = () => _testHost.GetDataPortal().PortalFailAsync(false); + var reflection = () => _testHost.GetDataPortal().FetchAsync(false); + + var generatedException = (await generated.Should().ThrowAsync()).Which; + var reflectionException = (await reflection.Should().ThrowAsync()).Which; + + ExceptionTypes(generatedException).Should().Equal(ExceptionTypes(reflectionException)); + ExceptionTypes(generatedException).Should().Contain(typeof(CallMethodException)); + generatedException.GetBaseException().Should().BeOfType(); + } + + [TestMethod] + public async Task FetchAsync_OperationThrowsNotSupported_IsNotInvokedAgain() + { + EdgeCaseRoot.FailCalls = 0; + + var act = () => _testHost.GetDataPortal().PortalFailAsync(true); + + (await act.Should().ThrowAsync()).Which + .GetBaseException().Should().BeOfType(); + EdgeCaseRoot.FailCalls.Should().Be(1); + } + + [TestMethod] + public async Task FetchAsync_ObjectArrayForSingleObjectCriteria_IsNotSplit() + { + var obj = await _testHost.GetDataPortal().PortalGetByValueAsync(new object[] { 1, "two" }); + + obj.Dispatch.Should().Be(DispatchPath.Named); + obj.Name.Should().Be("array:2"); + } + + [TestMethod] + public async Task FetchAsync_NullableInjectedService_IsOptional() + { + var obj = await _testHost.GetDataPortal().PortalGetOptionalAsync(5); + + obj.Dispatch.Should().Be(DispatchPath.Named); + obj.Name.Should().Be("5:"); + } + + private static List ExceptionTypes(Exception exception) + { + var result = new List(); + for (var current = exception; current is not null; current = current.InnerException) + result.Add(current.GetType()); + return result; + } + [TestMethod] public async Task FetchAsync_NonRunLocal_UsesConfiguredProxy() { @@ -471,7 +524,61 @@ private async Task LoadAsync(Guid key) _ = key; await Task.Yield(); } + + [Fetch] + private void Fail(bool notSupported) + { + _ = notSupported; + throw new InvalidOperationException("fail"); + } + } + + public interface IUnregisteredService { } + +#nullable enable + [DataPortalExtensions(Prefix = "Portal")] + public partial class EdgeCaseRoot : BusinessBase + { + public static int FailCalls; + + public static readonly PropertyInfo DispatchProperty = RegisterProperty(nameof(Dispatch)); + public string Dispatch + { + get => GetProperty(DispatchProperty); + private set => LoadProperty(DispatchProperty, value); + } + + public static readonly PropertyInfo NameProperty = RegisterProperty(nameof(Name)); + public string Name + { + get => GetProperty(NameProperty); + private set => LoadProperty(NameProperty, value); + } + + [Fetch] + private void Fail(bool notSupported) + { + Interlocked.Increment(ref FailCalls); + if (notSupported) + throw new DataPortalOperationNotSupportedException("Fail", null); + throw new InvalidOperationException("fail"); + } + + [Fetch] + private void GetByValue(object value) + { + Dispatch = DispatchPath.Current(); + Name = value is object[] array ? $"array:{array.Length}" : "single"; + } + + [Fetch] + private void GetOptional(int id, [Inject] IUnregisteredService? service) + { + Dispatch = DispatchPath.Current(); + Name = $"{id}:{(service is null ? "" : "service")}"; + } } +#nullable restore [DataPortalExtensions(Prefix = "Portal")] public partial class ExtensionCommand : CommandBase diff --git a/docs/analyzers/CSLA0025-UseGeneratedDataPortalExtensionAnalyzer.md b/docs/analyzers/CSLA0025-UseGeneratedDataPortalExtensionAnalyzer.md index f459fd88db..fe761ac7dd 100644 --- a/docs/analyzers/CSLA0025-UseGeneratedDataPortalExtensionAnalyzer.md +++ b/docs/analyzers/CSLA0025-UseGeneratedDataPortalExtensionAnalyzer.md @@ -30,6 +30,8 @@ The following methods are reported when the business type `T` has the `[DataPort * On `IDataPortal` and `DataPortal`: `Create`, `CreateAsync`, `Fetch`, `FetchAsync`, `Delete`, `DeleteAsync`, and the criteria overloads of `Execute` and `ExecuteAsync` * On `IChildDataPortal` and `DataPortal`: `CreateChild`, `CreateChildAsync`, `FetchChild`, and `FetchChildAsync` +A call is only reported when the generator produces an extension method that can replace it: the business type has an operation method of the matching kind (for example `[Fetch]` for `FetchAsync`) that accepts the number of criteria values passed, and an extension method is generated for that operation method. No diagnostic is reported when no such extension method exists, for example when the operation method has `[NoDataPortalExtension]`, has `ref` or `out` parameters, has a parameter type that is private to the business type, or has a name that the data portal interface's own methods would hide. + `Update`, `UpdateAsync`, `UpdateChild`, `UpdateChildAsync`, and `Execute`/`ExecuteAsync` called with a command object are not reported, because they do not take untyped criteria. Generated code is not analyzed, because the generated extension methods themselves call the untyped data portal methods. From 4c411d40383ae549c8c85ed124e0cc382d37a99d Mon Sep 17 00:00:00 2001 From: Rockford lhotka Date: Mon, 14 Sep 2026 01:57:52 -0500 Subject: [PATCH 07/10] #4359 Address second PR review on data portal generators - Resolve injected services inside the CallMethodException wrapper so resolution failures surface like reflection dispatch - Leave operation methods tied on injected parameter count to reflection, which reports an ambiguous match, and report CSLADP009 - Keep the type of short/ushort/byte/sbyte constants such as service keys - Only public business types state the extension class accessibility so partial parts from types mapping to the same class name merge - CSLA0025 checks argument conversions and skips operations that lose to an overload sharing their operation name Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01W6AuArAiFMmEoBmSNbD8YG --- ...UseGeneratedDataPortalExtensionAnalyzer.cs | 133 ++++++++++++++---- .../DataPortalExtensionsBuilder.cs | 8 +- .../DataPortalOperationsBuilder.cs | 56 +++++--- .../DataPortalOperationsDiagnostics.cs | 10 ++ ...ncrementalDataPortalOperationsGenerator.cs | 8 +- .../Models/OperationMethodModel.cs | 7 + .../OperationDiscovery.cs | 7 +- ...neratedDataPortalExtensionAnalyzerTests.cs | 62 ++++++++ .../DataPortalExtensionsGeneratorTests.cs | 30 ++++ ...r.Inner.DataPortalExtensions.g.verified.cs | 32 +++++ ...r_Inner.DataPortalExtensions.g.verified.cs | 32 +++++ ...sonEdit.DataPortalExtensions.g.verified.cs | 2 +- .../DataPortalOperationsGeneratorTests.cs | 30 ++++ ...sonEdit.DataPortalOperations.g.verified.cs | 72 ++++++++++ ...rTests.AmbiguousOperationName.verified.txt | 18 +++ ...ineItem.DataPortalOperations.g.verified.cs | 12 +- ...sonEdit.DataPortalOperations.g.verified.cs | 12 +- ...sonEdit.DataPortalOperations.g.verified.cs | 4 +- ...sonEdit.DataPortalOperations.g.verified.cs | 10 +- ...sonEdit.DataPortalOperations.g.verified.cs | 8 +- ...sonEdit.DataPortalOperations.g.verified.cs | 49 ++++++- ...sonEdit.DataPortalOperations.g.verified.cs | 4 +- ...sonEdit.DataPortalOperations.g.verified.cs | 4 +- .../DataPortal/DataPortalExtensionsTests.cs | 28 ++++ 24 files changed, 553 insertions(+), 85 deletions(-) create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ExtensionClassNameCollision#TestApp.Outer.Inner.DataPortalExtensions.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ExtensionClassNameCollision#TestApp.Outer_Inner.DataPortalExtensions.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOperationName.verified.txt diff --git a/Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzer.cs b/Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzer.cs index bddfb7d98c..463caccc18 100644 --- a/Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzer.cs +++ b/Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzer.cs @@ -101,7 +101,7 @@ private static void AnalyzeInvocation(OperationAnalysisContext context, KnownSym return; } - if (!HasGeneratedExtension(businessType, attribute, method.Name, GetCriteriaCount(invocation), symbols)) + if (!HasGeneratedExtension(businessType, attribute, method.Name, GetCriteriaArguments(invocation), context.Compilation, symbols)) { return; } @@ -151,9 +151,10 @@ private static bool IsCriteriaMethod(IMethodSymbol method, bool isRootPortal, bo /// /// Mirrors the data portal extensions generator: returns true when an /// extension method is generated for an operation method of the kind - /// called that accepts the number of criteria values passed. + /// called that accepts the criteria values passed. /// - private static bool HasGeneratedExtension(INamedTypeSymbol businessType, AttributeData attribute, string portalMethodName, int? criteriaCount, KnownSymbols symbols) + private static bool HasGeneratedExtension(INamedTypeSymbol businessType, AttributeData attribute, string portalMethodName, + ImmutableArray? criteriaArguments, Compilation compilation, KnownSymbols symbols) { if (businessType.IsAbstract || symbols.ICslaObject is null || !businessType.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, symbols.ICslaObject))) @@ -191,28 +192,31 @@ private static bool HasGeneratedExtension(INamedTypeSymbol businessType, Attribu : receiver.GetMembers().Select(m => m.Name).ToImmutableHashSet(); var operationAttributeName = $"Csla.{kind}Attribute"; - foreach (var operation in businessType.GetMembers().OfType()) + // Operation methods of this kind that generated code can dispatch to; + // ref/out parameters and a single object[] parameter are only invoked through reflection. + var operations = businessType.GetMembers() + .OfType() + .Where(m => m.MethodKind == MethodKind.Ordinary && !m.IsStatic && !m.IsGenericMethod) + .Where(m => m.GetAttributes().Any(a => a.AttributeClass?.ToDisplayString() == operationAttributeName)) + .Where(m => !m.Parameters.Any(p => p.RefKind is RefKind.Ref or RefKind.Out)) + .Where(m => !(m.Parameters.Length == 1 && m.Parameters[0].Type is IArrayTypeSymbol { Rank: 1, ElementType.SpecialType: SpecialType.System_Object })) + .Select(m => (Method: m, Criteria: m.Parameters.Where(p => !IsInjected(p, symbols.InjectAttribute)).ToList())) + .ToList(); + + foreach (var (operation, criteria) in operations) { - if (operation.MethodKind != MethodKind.Ordinary || operation.IsStatic || operation.IsGenericMethod) + if (operation.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, symbols.NoDataPortalExtensionAttribute))) { continue; } - var attributes = operation.GetAttributes(); - if (!attributes.Any(a => a.AttributeClass?.ToDisplayString() == operationAttributeName) || - attributes.Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, symbols.NoDataPortalExtensionAttribute))) + // Methods with the same criteria types differ only in injected parameters; + // the generator only produces an extension for the one the runtime prefers. + if (!IsPreferredAmongSameCriteria(operation, criteria, operations)) { continue; } - // ref/out parameters and a single object[] parameter are only invoked through reflection - if (operation.Parameters.Any(p => p.RefKind is RefKind.Ref or RefKind.Out) || - (operation.Parameters.Length == 1 && operation.Parameters[0].Type is IArrayTypeSymbol { Rank: 1, ElementType.SpecialType: SpecialType.System_Object })) - { - continue; - } - - var criteria = operation.Parameters.Where(p => !IsInjected(p, symbols.InjectAttribute)).ToList(); if (criteria.Any(p => IsPrivate(p.Type))) { continue; @@ -229,37 +233,112 @@ private static bool HasGeneratedExtension(INamedTypeSymbol businessType, Attribu continue; } - if (criteriaCount is { } count) + if (criteriaArguments is { } arguments && !AcceptsArguments(criteria, arguments, compilation)) + { + continue; + } + + return true; + } + + return false; + } + + private static bool IsPreferredAmongSameCriteria(IMethodSymbol operation, List criteria, + List<(IMethodSymbol Method, List Criteria)> operations) + { + var injectCount = operation.Parameters.Length - criteria.Count; + foreach (var (other, otherCriteria) in operations) + { + if (SymbolEqualityComparer.Default.Equals(other, operation) || + !otherCriteria.Select(p => p.Type).SequenceEqual(criteria.Select(p => p.Type), SymbolEqualityComparer.Default)) + { + continue; + } + + var otherInjectCount = other.Parameters.Length - otherCriteria.Count; + if (otherInjectCount > injectCount) + { + return false; + } + + // ties go to the first declared method + if (otherInjectCount == injectCount && + operations.FindIndex(o => SymbolEqualityComparer.Default.Equals(o.Method, other)) < + operations.FindIndex(o => SymbolEqualityComparer.Default.Equals(o.Method, operation))) + { + return false; + } + } + + return true; + } + + /// + /// The criteria values can be passed to the criteria parameters of the + /// generated extension method. + /// + private static bool AcceptsArguments(List criteria, ImmutableArray arguments, Compilation compilation) + { + var required = criteria.Count(p => !p.HasExplicitDefaultValue && !p.IsParams); + var hasParams = criteria.Count > 0 && criteria[criteria.Count - 1].IsParams; + if (arguments.Length < required || (!hasParams && arguments.Length > criteria.Count)) + { + return false; + } + + for (var i = 0; i < arguments.Length; i++) + { + var parameter = criteria[Math.Min(i, criteria.Count - 1)]; + var argument = arguments[i] is IConversionOperation { IsImplicit: true } conversion ? conversion.Operand : arguments[i]; + if (parameter.IsParams && parameter.Type is IArrayTypeSymbol paramsArray) { - var required = criteria.Count(p => !p.HasExplicitDefaultValue && !p.IsParams); - var hasParams = criteria.Count > 0 && criteria[criteria.Count - 1].IsParams; - if (count < required || (!hasParams && count > criteria.Count)) + var isLastExpandable = i >= criteria.Count - 1; + if (isLastExpandable && (IsConvertible(argument, paramsArray.ElementType, compilation) || + (arguments.Length == criteria.Count && IsConvertible(argument, paramsArray, compilation)))) { continue; } + + return false; } - return true; + if (!IsConvertible(argument, parameter.Type, compilation)) + { + return false; + } } - return false; + return true; + } + + private static bool IsConvertible(IOperation argument, ITypeSymbol target, Compilation compilation) + { + if (argument.Type is null) + { + // null literal or a typeless expression + return argument.ConstantValue is not { HasValue: true, Value: null } || + target.IsReferenceType || target.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T; + } + + return compilation.ClassifyCommonConversion(argument.Type, target).IsImplicit; } /// - /// The number of criteria values passed to the data portal method, or - /// null when they are passed as an existing array. + /// The criteria values passed to the data portal method, or null when + /// they are passed as an existing array. /// - private static int? GetCriteriaCount(IInvocationOperation invocation) + private static ImmutableArray? GetCriteriaArguments(IInvocationOperation invocation) { if (invocation.Arguments.Length == 0) { - return 0; + return ImmutableArray.Empty; } var argument = invocation.Arguments[invocation.Arguments.Length - 1]; if (argument.ArgumentKind == ArgumentKind.ParamArray && argument.Value is IArrayCreationOperation { Initializer: { } initializer }) { - return initializer.ElementValues.Length; + return initializer.ElementValues; } return null; diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsBuilder.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsBuilder.cs index e1307567d0..f863f6ffcc 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsBuilder.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsBuilder.cs @@ -77,11 +77,15 @@ public static (string? Source, List Diagnostics) Build(Extension OpenBlock(writer); } - var classVisibility = type.Visibility == TypeVisibility.Public ? "public" : "internal"; + // Different business types can map to the same class name (for example + // a top-level Outer_Inner and a nested Outer.Inner). The partial parts + // then merge; only a public business type states the accessibility so + // the parts never conflict, and the methods carry their own accessibility. + var classModifiers = type.Visibility == TypeVisibility.Public ? "public static partial" : "static partial"; writer.WriteLine("/// "); writer.WriteLine($"/// Strongly typed data portal extension methods for ."); writer.WriteLine("/// "); - writer.WriteLine($"{classVisibility} static partial class {type.ExtensionClassName}"); + writer.WriteLine($"{classModifiers} class {type.ExtensionClassName}"); OpenBlock(writer); var isFirst = true; diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsBuilder.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsBuilder.cs index 27402bf7c2..90ba4ae6b7 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsBuilder.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsBuilder.cs @@ -91,17 +91,21 @@ public static string Build(OperationTypeModel model) } /// - /// Selects the method used for each operation name in name-based + /// Selects the method used for each operation name in generated /// dispatch. Like the runtime method resolution, when several methods /// share an operation name (they differ only in injected parameters) - /// the one with the most injected parameters wins; ties go to the - /// first declared. Returns the selected methods and any collisions. + /// the one with the most injected parameters wins. When several methods + /// tie for the most injected parameters the runtime reports an ambiguous + /// match, so none of them is selected and the call is left to + /// reflection-based dispatch; the first declared of them is reported as + /// the winner of an ambiguous collision. Returns the selected methods + /// and any collisions. /// - public static (List Selected, List<(OperationMethodModel Winner, OperationMethodModel Loser)> Collisions) + public static (List Selected, List Collisions) SelectNamedDispatchMethods(EquatableArray methods) { var selected = new List(); - var collisions = new List<(OperationMethodModel, OperationMethodModel)>(); + var collisions = new List(); foreach (var group in methods.Where(m => m.CanDispatchByName).GroupBy(m => m.OperationName)) { var ordered = group.ToList(); @@ -111,13 +115,31 @@ public static (List Selected, List<(OperationMethodModel W if (candidate.InjectCount > winner.InjectCount) winner = candidate; } - selected.Add(winner); + var isAmbiguous = ordered.Count(m => m.InjectCount == winner.InjectCount) > 1; + if (!isAmbiguous) + selected.Add(winner); foreach (var loser in ordered.Where(m => !ReferenceEquals(m, winner))) - collisions.Add((winner, loser)); + collisions.Add(new OperationCollision(winner, loser, isAmbiguous)); } return (selected, collisions); } + /// + /// Methods that do not take part in generated dispatch because another + /// method shares their operation name. + /// + public static HashSet GetExcludedFromDispatch(List collisions) + { + var result = new HashSet(ReferenceEqualityComparer.Instance); + foreach (var collision in collisions) + { + result.Add(collision.Loser); + if (collision.IsAmbiguous) + result.Add(collision.Winner); + } + return result; + } + private static Dictionary GetInterfaceMemberNames(EquatableArray methods) { var result = new Dictionary(ReferenceEqualityComparer.Instance); @@ -172,8 +194,8 @@ private static void AppendInvokeOperationAsync(IndentedTextWriter writer, Operat // Methods sharing an operation name differ only in injected parameters; // only the one the runtime would select takes part in dispatch. var (_, collisions) = SelectNamedDispatchMethods(model.Methods); - var losers = new HashSet(collisions.Select(c => c.Loser), ReferenceEqualityComparer.Instance); - var dispatchable = model.Methods.Where(m => m.CanDispatch && !losers.Contains(m)).ToList(); + var excluded = GetExcludedFromDispatch(collisions); + var dispatchable = model.Methods.Where(m => m.CanDispatch && !excluded.Contains(m)).ToList(); if (dispatchable.Count > 0) writer.WriteLine($"var {OperationsLocal} = ({OperationDiscovery.OperationsInterfaceName})this;"); @@ -267,6 +289,14 @@ private static void AppendDispatch(IndentedTextWriter writer, OperationMethodMod if (method.IsAsync) writer.WriteLine($"{HelperType}.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, \"{method.MethodName.TrimStart('@')}\");"); + // Like reflection-based dispatch, exceptions thrown while resolving + // injected services or by the operation method are wrapped in + // CallMethodException. This also keeps an operation method throwing + // DataPortalOperationNotSupportedException from being treated as an + // unmatched operation and invoked again through reflection. + writer.WriteLine("try"); + OpenBlock(writer); + var arguments = new List(); var criteriaIndex = 0; var injectIndex = 0; @@ -303,13 +333,7 @@ private static void AppendDispatch(IndentedTextWriter writer, OperationMethodMod } } - // Like reflection-based dispatch, exceptions thrown by the operation - // method are wrapped in CallMethodException. This also keeps an operation - // method throwing DataPortalOperationNotSupportedException from being - // treated as an unmatched operation and invoked again through reflection. var call = $"{OperationsLocal}.{memberName}({string.Join(", ", arguments)})"; - writer.WriteLine("try"); - OpenBlock(writer); writer.WriteLine(method.IsAsync ? $"await {call}.ConfigureAwait(false);" : $"{call};"); CloseBlock(writer); writer.WriteLine($"catch (global::System.Exception {ExceptionLocal})"); @@ -353,7 +377,7 @@ private static void CloseBlock(IndentedTextWriter writer) writer.WriteLine("}"); } - private sealed class ReferenceEqualityComparer : IEqualityComparer + internal sealed class ReferenceEqualityComparer : IEqualityComparer { public static readonly ReferenceEqualityComparer Instance = new(); diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs index 2468d6de65..aca0da279d 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs @@ -25,6 +25,7 @@ internal static class DataPortalOperationsDiagnostics public const string InaccessibleParameterTypeId = "CSLADP006"; public const string DuplicateExtensionMethodId = "CSLADP007"; public const string InvalidExtensionPrefixId = "CSLADP008"; + public const string AmbiguousOperationNameId = "CSLADP009"; public static readonly DiagnosticDescriptor DuplicateOperationName = new( id: DuplicateOperationNameId, @@ -82,6 +83,14 @@ internal static class DataPortalOperationsDiagnostics defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true); + public static readonly DiagnosticDescriptor AmbiguousOperationName = new( + id: AmbiguousOperationNameId, + title: "Data portal operation methods are ambiguous", + messageFormat: "Methods '{0}' and '{1}' on '{2}' both map to data portal operation '{3}' with the same number of injected parameters; the data portal cannot choose between them and reports an ambiguous match", + category: Category, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + public static DiagnosticDescriptor GetDescriptor(string id) => id switch { DuplicateOperationNameId => DuplicateOperationName, @@ -91,6 +100,7 @@ internal static class DataPortalOperationsDiagnostics InaccessibleParameterTypeId => InaccessibleParameterType, DuplicateExtensionMethodId => DuplicateExtensionMethod, InvalidExtensionPrefixId => InvalidExtensionPrefix, + AmbiguousOperationNameId => AmbiguousOperationName, _ => throw new ArgumentOutOfRangeException(nameof(id), id, null) }; } diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/IncrementalDataPortalOperationsGenerator.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/IncrementalDataPortalOperationsGenerator.cs index 3aec07868e..2dabcda2cf 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/IncrementalDataPortalOperationsGenerator.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/IncrementalDataPortalOperationsGenerator.cs @@ -36,12 +36,12 @@ public void Initialize(IncrementalGeneratorInitializationContext context) context.RegisterSourceOutput(types, static (spc, model) => { var (_, collisions) = DataPortalOperationsBuilder.SelectNamedDispatchMethods(model.Methods); - foreach (var (winner, loser) in collisions) + foreach (var collision in collisions) { spc.ReportDiagnostic(Diagnostic.Create( - DataPortalOperationsDiagnostics.DuplicateOperationName, - loser.Location?.ToLocation(), - winner.MethodDisplay, loser.MethodDisplay, model.Type.TypeName, winner.OperationName)); + collision.IsAmbiguous ? DataPortalOperationsDiagnostics.AmbiguousOperationName : DataPortalOperationsDiagnostics.DuplicateOperationName, + collision.Loser.Location?.ToLocation(), + collision.Winner.MethodDisplay, collision.Loser.MethodDisplay, model.Type.TypeName, collision.Winner.OperationName)); } spc.AddSource( diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/OperationMethodModel.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/OperationMethodModel.cs index 0a6b9b4dd0..cfffac2c03 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/OperationMethodModel.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/OperationMethodModel.cs @@ -8,6 +8,13 @@ namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations.Models { + /// + /// Two operation methods that share an operation name. When + /// is true the runtime cannot choose between + /// the methods, and is only the first declared. + /// + internal sealed record OperationCollision(OperationMethodModel Winner, OperationMethodModel Loser, bool IsAmbiguous); + /// /// A data portal operation method as seen through one operation /// attribute. A method carrying several operation attributes diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs index f41fccf06b..6f8db9420c 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs @@ -438,7 +438,12 @@ double d when double.IsNegativeInfinity(d) => "double.NegativeInfinity", long l => l.ToString(CultureInfo.InvariantCulture) + "L", ulong ul => ul.ToString(CultureInfo.InvariantCulture) + "UL", uint ui => ui.ToString(CultureInfo.InvariantCulture) + "U", - int or short or ushort or byte or sbyte => Convert.ToString(value, CultureInfo.InvariantCulture), + int i => i.ToString(CultureInfo.InvariantCulture), + // Casts keep the type of boxed values such as keyed service keys. + short sh => $"(short)({sh.ToString(CultureInfo.InvariantCulture)})", + ushort us => $"(ushort){us.ToString(CultureInfo.InvariantCulture)}", + byte b8 => $"(byte){b8.ToString(CultureInfo.InvariantCulture)}", + sbyte sb => $"(sbyte)({sb.ToString(CultureInfo.InvariantCulture)})", _ => null }; } diff --git a/Source/tests/Csla.Analyzers.Tests/UseGeneratedDataPortalExtensionAnalyzerTests.cs b/Source/tests/Csla.Analyzers.Tests/UseGeneratedDataPortalExtensionAnalyzerTests.cs index 5fbd87297a..d2ff24de56 100644 --- a/Source/tests/Csla.Analyzers.Tests/UseGeneratedDataPortalExtensionAnalyzerTests.cs +++ b/Source/tests/Csla.Analyzers.Tests/UseGeneratedDataPortalExtensionAnalyzerTests.cs @@ -255,6 +255,68 @@ public async Task AnalyzeWhenCriteriaCountDoesNotMatchAnyExtension() await TestHelpers.RunAnalysisAsync(code, []); } + [TestMethod] + public async Task AnalyzeWhenCriteriaTypesDoNotMatchAnyExtension() + { + var code = CreateCode( + """ + await portal.FetchAsync("x"); + childPortal.FetchChild(1.5); + """); + await TestHelpers.RunAnalysisAsync(code, []); + } + + [TestMethod] + public async Task AnalyzeWhenCriteriaConvertToExtensionParameters() + { + var code = CreateCode( + """ + short id = 1; + await portal.FetchAsync(id); + IDataPortal dPortal = null; + await dPortal.FetchAsync(null, "a", "b"); + """, + additionalTypes: + """ + [DataPortalExtensions] + [Serializable] + public partial class D : BusinessBase + { + [Fetch] + private void GetByNames(string category, params string[] names) { } + } + """); + await TestHelpers.RunAnalysisAsync(code, + [Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension, Constants.AnalyzerIdentifiers.UseGeneratedDataPortalExtension]); + } + + [TestMethod] + public async Task AnalyzeWhenPreferredOverloadHasNoDataPortalExtension() + { + var code = CreateCode( + """ + IDataPortal dPortal = null; + await dPortal.FetchAsync(1); + """, + additionalTypes: + """ + public interface IDal { } + + [DataPortalExtensions] + [Serializable] + public partial class D : BusinessBase + { + [Fetch] + private void GetById(int id) { } + + [NoDataPortalExtension] + [Fetch] + private void GetById(int id, [Inject] IDal dal) { } + } + """); + await TestHelpers.RunAnalysisAsync(code, []); + } + [TestMethod] public async Task AnalyzeWhenNoExtensionIsGeneratedForTheOperation() { diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/DataPortalExtensionsGeneratorTests.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/DataPortalExtensionsGeneratorTests.cs index 145356e3bb..8c1d74ae9a 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/DataPortalExtensionsGeneratorTests.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/DataPortalExtensionsGeneratorTests.cs @@ -321,6 +321,36 @@ private void GetById(int id) { } await Verify(source); } + [TestMethod("Business types mapping to the same extension class name merge into one partial class")] + public async Task ExtensionClassNameCollision() + { + var source = """ + using Csla; + + namespace TestApp + { + [DataPortalExtensions] + public partial class Outer_Inner : BusinessBase + { + [Fetch] + private void GetById(int id) { } + } + + internal partial class Outer + { + [DataPortalExtensions] + public partial class Inner : BusinessBase + { + [Fetch] + private void GetById(int id) { } + } + } + } + """; + + await Verify(source); + } + [TestMethod("Internal business type generates an internal extension class")] public async Task InternalType() { diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ExtensionClassNameCollision#TestApp.Outer.Inner.DataPortalExtensions.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ExtensionClassNameCollision#TestApp.Outer.Inner.DataPortalExtensions.g.verified.cs new file mode 100644 index 0000000000..180184772f --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ExtensionClassNameCollision#TestApp.Outer.Inner.DataPortalExtensions.g.verified.cs @@ -0,0 +1,32 @@ +//HintName: TestApp.Outer.Inner.DataPortalExtensions.g.cs +// +#nullable enable + +namespace TestApp +{ + /// + /// Strongly typed data portal extension methods for . + /// + static partial class Outer_InnerDataPortalExtensions + { + /// + /// Invokes the GetById(int) Fetch operation of . + /// + internal static global::System.Threading.Tasks.Task GetByIdAsync(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperationAsync("Fetch__Int32", false, new object?[] { id }); + return portal.FetchAsync(new object?[] { id }); + } + + /// + /// Invokes the GetById(int) Fetch operation of . + /// + internal static global::TestApp.Outer.Inner GetById(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperation("Fetch__Int32", false, new object?[] { id }); + return portal.Fetch(new object?[] { id }); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ExtensionClassNameCollision#TestApp.Outer_Inner.DataPortalExtensions.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ExtensionClassNameCollision#TestApp.Outer_Inner.DataPortalExtensions.g.verified.cs new file mode 100644 index 0000000000..ad344da14e --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ExtensionClassNameCollision#TestApp.Outer_Inner.DataPortalExtensions.g.verified.cs @@ -0,0 +1,32 @@ +//HintName: TestApp.Outer_Inner.DataPortalExtensions.g.cs +// +#nullable enable + +namespace TestApp +{ + /// + /// Strongly typed data portal extension methods for . + /// + public static partial class Outer_InnerDataPortalExtensions + { + /// + /// Invokes the GetById(int) Fetch operation of . + /// + public static global::System.Threading.Tasks.Task GetByIdAsync(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperationAsync("Fetch__Int32", false, new object?[] { id }); + return portal.FetchAsync(new object?[] { id }); + } + + /// + /// Invokes the GetById(int) Fetch operation of . + /// + public static global::TestApp.Outer_Inner GetById(this global::Csla.IDataPortal portal, int id) + { + if (portal is global::Csla.IDataPortalOperationInvoker __invoker) + return __invoker.FetchByOperation("Fetch__Int32", false, new object?[] { id }); + return portal.Fetch(new object?[] { id }); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InternalType#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InternalType#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs index c7be478bfe..223676d17b 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InternalType#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InternalType#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs @@ -7,7 +7,7 @@ namespace TestApp /// /// Strongly typed data portal extension methods for . /// - internal static partial class PersonEditDataPortalExtensions + static partial class PersonEditDataPortalExtensions { /// /// Invokes the GetById(int) Fetch operation of . diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsGeneratorTests.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsGeneratorTests.cs index 74f502ff14..747fac11b5 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsGeneratorTests.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsGeneratorTests.cs @@ -500,6 +500,9 @@ public partial class PersonEdit : Csla.BusinessBase { [Fetch] private void Fetch(int id, [Inject(Key = "primary")] IDal dal, [Inject(Key = DalKind.Secondary, AllowNull = true)] IDal? secondary) { } + + [Create] + private void Create([Inject(Key = (short)-1)] IDal dal, [Inject(Key = (byte)2)] IDal other) { } } } """; @@ -534,6 +537,33 @@ private void Load(int id) { } await DataPortalOperationsTestHelper.VerifyWithDiagnostics(source); } + [TestMethod("Overloads tied on injected parameters are left to reflection and reported")] + public async Task AmbiguousOperationName() + { + var source = """ + using Csla; + + namespace TestApp + { + public interface IDal { } + + public partial class PersonEdit : Csla.BusinessBase + { + [Fetch] + private void Fetch(int id, [Inject] IDal dal) { } + + [Fetch] + private void Load(int id, [Inject] IDal dal) { } + + [Fetch] + private void Fetch(string name) { } + } + } + """; + + await DataPortalOperationsTestHelper.VerifyWithDiagnostics(source); + } + [TestMethod("Overloads with the same criteria count require exact types in type-based dispatch")] public async Task AmbiguousOverloads() { diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs new file mode 100644 index 0000000000..60bf1357c6 --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -0,0 +1,72 @@ +//HintName: TestApp.PersonEdit.DataPortalOperations.g.cs +// +#nullable enable +#pragma warning disable CS1998 // Async method lacks 'await' operators + +namespace TestApp +{ + partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, global::Csla.Server.IDataPortalOperationMapping, global::Csla.Server.IDataPortalOperationNamedMapping + { + /// + /// Data portal operation methods declared by this type. + /// Generated by CSLA .NET so the methods are statically referenced. + /// + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + internal interface IDataPortalOperations + { + void Fetch__Int32(int id, global::TestApp.IDal dal); + void Fetch__Int32_2(int id, global::TestApp.IDal dal); + void Fetch__String(string name); + } + + void IDataPortalOperations.Fetch__Int32(int id, global::TestApp.IDal dal) => Fetch(id, dal); + void IDataPortalOperations.Fetch__Int32_2(int id, global::TestApp.IDal dal) => Load(id, dal); + void IDataPortalOperations.Fetch__String(string name) => Fetch(name); + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( + global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is string __c0p0) + { + try + { + __operations.Fetch__String(__c0p0); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } + return; + } + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationType, criteria); + } + + async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationNamedMapping.InvokeNamedOperationAsync( + string operationName, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) + { + var __operations = (IDataPortalOperations)this; + switch (operationName) + { + case "Fetch__String": + if (criteria is { Length: 1 } && (criteria[0] is null || criteria[0] is string)) + { + try + { + __operations.Fetch__String((string)criteria[0]!); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Fetch", __ex); + } + return; + } + break; + } + throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); + } + } +} diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOperationName.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOperationName.verified.txt new file mode 100644 index 0000000000..861cf184dd --- /dev/null +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOperationName.verified.txt @@ -0,0 +1,18 @@ +{ + Diagnostics: [ + { + Location: Source0.cs: (12,17)-(12,21), + Message: Methods 'Fetch(int, IDal)' and 'Load(int, IDal)' on 'PersonEdit' both map to data portal operation 'Fetch__Int32' with the same number of injected parameters; the data portal cannot choose between them and reports an ambiguous match, + Severity: Warning, + WarningLevel: 1, + Descriptor: { + Id: CSLADP009, + Title: Data portal operation methods are ambiguous, + MessageFormat: Methods '{0}' and '{1}' on '{2}' both map to data portal operation '{3}' with the same number of injected parameters; the data portal cannot choose between them and reports an ambiguous match, + Category: Csla.DataPortal, + DefaultSeverity: Warning, + IsEnabledByDefault: true + } + } + ] +} \ No newline at end of file diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs index ea4de6bb0d..f724a4e024 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs @@ -66,9 +66,9 @@ internal interface IDataPortalOperations if (criteria is null or { Length: 0 }) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "InsertChild"); - var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; try { + var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; await __operations.InsertChild(__c2i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -83,9 +83,9 @@ internal interface IDataPortalOperations if (criteria is null or { Length: 0 }) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "UpdateChild"); - var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; try { + var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; await __operations.UpdateChild(__c3i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -100,9 +100,9 @@ internal interface IDataPortalOperations if (criteria is null or { Length: 0 }) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "DeleteSelfChild"); - var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; try { + var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; await __operations.DeleteSelfChild(__c4i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -153,9 +153,9 @@ internal interface IDataPortalOperations if (criteria is null or { Length: 0 }) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "InsertChild"); - var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; try { + var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; await __operations.InsertChild(__c2i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -169,9 +169,9 @@ internal interface IDataPortalOperations if (criteria is null or { Length: 0 }) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "UpdateChild"); - var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; try { + var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; await __operations.UpdateChild(__c3i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -185,9 +185,9 @@ internal interface IDataPortalOperations if (criteria is null or { Length: 0 }) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "DeleteSelfChild"); - var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; try { + var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; await __operations.DeleteSelfChild(__c4i0).ConfigureAwait(false); } catch (global::System.Exception __ex) diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 5721e68e15..37239bf8b7 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -66,9 +66,9 @@ internal interface IDataPortalOperations if (criteria is null or { Length: 0 }) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Insert"); - var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; try { + var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; await __operations.Insert(__c2i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -83,9 +83,9 @@ internal interface IDataPortalOperations if (criteria is null or { Length: 0 }) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Update"); - var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; try { + var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; await __operations.Update(__c3i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -100,9 +100,9 @@ internal interface IDataPortalOperations if (criteria is null or { Length: 0 }) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "DeleteSelf"); - var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; try { + var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; await __operations.DeleteSelf(__c4i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -153,9 +153,9 @@ internal interface IDataPortalOperations if (criteria is null or { Length: 0 }) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Insert"); - var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; try { + var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; await __operations.Insert(__c2i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -169,9 +169,9 @@ internal interface IDataPortalOperations if (criteria is null or { Length: 0 }) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Update"); - var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; try { + var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; await __operations.Update(__c3i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -185,9 +185,9 @@ internal interface IDataPortalOperations if (criteria is null or { Length: 0 }) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "DeleteSelf"); - var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; try { + var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; await __operations.DeleteSelf(__c4i0).ConfigureAwait(false); } catch (global::System.Exception __ex) diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 89a45ec7b0..8e23c1ed83 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -31,9 +31,9 @@ internal interface IDataPortalOperations { if (criteria is { Length: 1 } && criteria[0] is int __c0p0) { - var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; try { + var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; __operations.Fetch__Int32(__c0p0, __c0i0); } catch (global::System.Exception __ex) @@ -55,9 +55,9 @@ internal interface IDataPortalOperations case "Fetch__Int32": if (criteria is { Length: 1 } && criteria[0] is int __c0p0) { - var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; try { + var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; __operations.Fetch__Int32(__c0p0, __c0i0); } catch (global::System.Exception __ex) diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InCriteriaAndNullableInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InCriteriaAndNullableInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index a46002eed3..36f4686d43 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InCriteriaAndNullableInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InCriteriaAndNullableInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -29,10 +29,10 @@ internal interface IDataPortalOperations { if (criteria is null or { Length: 0 }) { - var __c0i0 = (int?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(int?), true)); - var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), true)); try { + var __c0i0 = (int?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(int?), true)); + var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), true)); __operations.Create(__c0i0, __c0i1); } catch (global::System.Exception __ex) @@ -69,9 +69,9 @@ internal interface IDataPortalOperations case "Fetch__String_Int32": if (criteria is { Length: 2 } && (criteria[0] is null || criteria[0] is string) && criteria[1] is int __c0p1) { - var __c0p0 = (string?)criteria[0]!; try { + var __c0p0 = (string?)criteria[0]!; __operations.Fetch__String_Int32(in __c0p0, in __c0p1); } catch (global::System.Exception __ex) @@ -84,10 +84,10 @@ internal interface IDataPortalOperations case "Create": if (criteria is null or { Length: 0 }) { - var __c1i0 = (int?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(int?), true)); - var __c1i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), true)); try { + var __c1i0 = (int?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(int?), true)); + var __c1i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), true)); __operations.Create(__c1i0, __c1i1); } catch (global::System.Exception __ex) diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InjectBeforeCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InjectBeforeCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 7fc847e724..1a53d1a8ae 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InjectBeforeCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InjectBeforeCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -27,10 +27,10 @@ internal interface IDataPortalOperations { if (criteria is { Length: 2 } && criteria[0] is int __c0p0 && criteria[1] is string __c0p1) { - var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), true)); try { + var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), true)); __operations.Fetch__Int32_String(__c0i0, __c0p0, __c0i1, __c0p1); } catch (global::System.Exception __ex) @@ -52,10 +52,10 @@ internal interface IDataPortalOperations case "Fetch__Int32_String": if (criteria is { Length: 2 } && criteria[0] is int __c0p0 && (criteria[1] is null || criteria[1] is string)) { - var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), true)); try { + var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), true)); __operations.Fetch__Int32_String(__c0i0, __c0p0, __c0i1, (string)criteria[1]!); } catch (global::System.Exception __ex) diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.KeyedInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.KeyedInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index d11894ae25..4e8d387876 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.KeyedInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.KeyedInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -15,23 +15,42 @@ partial class PersonEdit : global::TestApp.PersonEdit.IDataPortalOperations, glo internal interface IDataPortalOperations { void Fetch__Int32(int id, global::TestApp.IDal dal, global::TestApp.IDal? secondary); + void Create(global::TestApp.IDal dal, global::TestApp.IDal other); } void IDataPortalOperations.Fetch__Int32(int id, global::TestApp.IDal dal, global::TestApp.IDal? secondary) => Fetch(id, dal, secondary); + void IDataPortalOperations.Create(global::TestApp.IDal dal, global::TestApp.IDal other) => Create(dal, other); async global::System.Threading.Tasks.Task global::Csla.Server.IDataPortalOperationMapping.InvokeOperationAsync( global::System.Type operationType, bool isSync, object?[]? criteria, global::System.IServiceProvider serviceProvider) { var __operations = (IDataPortalOperations)this; - if (operationType == typeof(global::Csla.FetchAttribute)) + if (operationType == typeof(global::Csla.CreateAttribute)) { - if (criteria is { Length: 1 } && criteria[0] is int __c0p0) + if (criteria is null or { Length: 0 }) { - var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), "primary", false))!; - var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), global::TestApp.DalKind.Secondary, true)); try { - __operations.Fetch__Int32(__c0p0, __c0i0, __c0i1); + var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), (short)(-1), false))!; + var __c0i1 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), (byte)2, false))!; + __operations.Create(__c0i0, __c0i1); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } + return; + } + } + else if (operationType == typeof(global::Csla.FetchAttribute)) + { + if (criteria is { Length: 1 } && criteria[0] is int __c1p0) + { + try + { + var __c1i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), "primary", false))!; + var __c1i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), global::TestApp.DalKind.Secondary, true)); + __operations.Fetch__Int32(__c1p0, __c1i0, __c1i1); } catch (global::System.Exception __ex) { @@ -52,10 +71,10 @@ internal interface IDataPortalOperations case "Fetch__Int32": if (criteria is { Length: 1 } && criteria[0] is int __c0p0) { - var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), "primary", false))!; - var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), global::TestApp.DalKind.Secondary, true)); try { + var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), "primary", false))!; + var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), global::TestApp.DalKind.Secondary, true)); __operations.Fetch__Int32(__c0p0, __c0i0, __c0i1); } catch (global::System.Exception __ex) @@ -65,6 +84,22 @@ internal interface IDataPortalOperations return; } break; + case "Create": + if (criteria is null or { Length: 0 }) + { + try + { + var __c1i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), (short)(-1), false))!; + var __c1i1 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), (byte)2, false))!; + __operations.Create(__c1i0, __c1i1); + } + catch (global::System.Exception __ex) + { + throw global::Csla.Server.DataPortalOperationHelper.CreateCallMethodException(this, "Create", __ex); + } + return; + } + break; } throw new global::Csla.Server.DataPortalOperationNotSupportedException(operationName, criteria); } diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index b84a6d0671..aff3acd97a 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -28,9 +28,9 @@ internal interface IDataPortalOperations if (criteria is { Length: 1 } && criteria[0] is int __c0p0) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); - var __c0i0 = (global::TestApp.ILogger?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.ILogger), true)); try { + var __c0i0 = (global::TestApp.ILogger?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.ILogger), true)); await __operations.Fetch__Int32(__c0p0, __c0i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -53,9 +53,9 @@ internal interface IDataPortalOperations if (criteria is { Length: 1 } && criteria[0] is int __c0p0) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); - var __c0i0 = (global::TestApp.ILogger?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.ILogger), true)); try { + var __c0i0 = (global::TestApp.ILogger?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.ILogger), true)); await __operations.Fetch__Int32(__c0p0, __c0i0).ConfigureAwait(false); } catch (global::System.Exception __ex) diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index d7f044136d..64a47cc28c 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -28,9 +28,9 @@ internal interface IDataPortalOperations if (criteria is null or { Length: 0 }) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Insert"); - var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; try { + var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; await __operations.Insert(__c0i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -53,9 +53,9 @@ internal interface IDataPortalOperations if (criteria is null or { Length: 0 }) { global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Insert"); - var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; try { + var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; await __operations.Insert(__c0i0).ConfigureAwait(false); } catch (global::System.Exception __ex) diff --git a/Source/tests/csla.netcore.test/DataPortal/DataPortalExtensionsTests.cs b/Source/tests/csla.netcore.test/DataPortal/DataPortalExtensionsTests.cs index 3823c251b7..7b11c374ae 100644 --- a/Source/tests/csla.netcore.test/DataPortal/DataPortalExtensionsTests.cs +++ b/Source/tests/csla.netcore.test/DataPortal/DataPortalExtensionsTests.cs @@ -145,6 +145,20 @@ public async Task FetchAsync_OperationThrows_WrapsExceptionLikeReflectionDispatc generatedException.GetBaseException().Should().BeOfType(); } + [TestMethod] + public async Task FetchAsync_MissingRequiredService_WrapsExceptionLikeReflectionDispatch() + { + var generated = () => _testHost.GetDataPortal().PortalGetRequiredAsync("code"); + var reflection = () => _testHost.GetDataPortal().FetchAsync("code"); + + var generatedException = (await generated.Should().ThrowAsync()).Which; + var reflectionException = (await reflection.Should().ThrowAsync()).Which; + + ExceptionTypes(generatedException).Should().Equal(ExceptionTypes(reflectionException)); + ExceptionTypes(generatedException).Should().Contain(typeof(CallMethodException)); + generatedException.GetBaseException().Should().BeOfType(); + } + [TestMethod] public async Task FetchAsync_OperationThrowsNotSupported_IsNotInvokedAgain() { @@ -531,6 +545,13 @@ private void Fail(bool notSupported) _ = notSupported; throw new InvalidOperationException("fail"); } + + [Fetch] + private void GetRequired(string code, [Inject] IUnregisteredService service) + { + _ = code; + _ = service; + } } public interface IUnregisteredService { } @@ -571,6 +592,13 @@ private void GetByValue(object value) Name = value is object[] array ? $"array:{array.Length}" : "single"; } + [Fetch] + private void GetRequired(string code, [Inject] IUnregisteredService service) + { + _ = code; + _ = service; + } + [Fetch] private void GetOptional(int id, [Inject] IUnregisteredService? service) { From cbe92b03ad76e9033dc03dcc192b5751fbdd0cbe Mon Sep 17 00:00:00 2001 From: Rockford lhotka Date: Tue, 15 Sep 2026 10:07:57 -0500 Subject: [PATCH 08/10] #4359 Report data portal diagnostics from analyzers and allow async-only extensions - Move the CSLADP002-CSLADP009 diagnostics out of the data portal generators into DataPortalOperationsAnalyzer and DataPortalExtensionsAnalyzer, which share the generators' selection rules. Diagnostics now use source locations, so they can be suppressed with #pragma. - Add the CslaGenerateSyncDataPortalExtensions MSBuild property (default true); set it to false to generate only async [DataPortalExtensions] methods. CSLA0025 no longer reports sync calls when sync extensions are not generated. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01W6AuArAiFMmEoBmSNbD8YG --- ...UseGeneratedDataPortalExtensionAnalyzer.cs | 18 ++++ .../DataPortalExtensionsAnalyzer.cs | 77 ++++++++++++++++ .../DataPortalExtensionsBuilder.cs | 88 +++++++++++-------- .../ExtensionTypeModel.cs | 3 +- ...ncrementalDataPortalExtensionsGenerator.cs | 57 +++++++++--- .../DataPortalOperationsAnalyzer.cs | 73 +++++++++++++++ .../DataPortalOperationsDiagnostics.cs | 7 +- ...ncrementalDataPortalOperationsGenerator.cs | 29 +----- .../Models/LocationInfo.cs | 21 ++++- .../OperationDiscovery.cs | 64 ++++++++++++++ .../DataPortalOperations/TrackingNames.cs | 3 + Source/Csla/buildTransitive/Csla.targets | 9 ++ .../tests/Csla.Analyzers.Tests/TestHelpers.cs | 27 +++++- ...neratedDataPortalExtensionAnalyzerTests.cs | 16 ++++ .../DataPortalExtensionsGeneratorTests.cs | 40 ++++++++- ...sonEdit.DataPortalExtensions.g.verified.cs | 42 +++++++++ ...sionsGeneratorTests.AsyncOnly.verified.txt | 45 ++++++++++ ...ratorTests.DuplicateExtension.verified.txt | 21 ++++- ...ratorTests.GenericTypeSkipped.verified.txt | 9 +- ...onsGeneratorTests.HiddenNames.verified.txt | 41 +++++++-- ...sGeneratorTests.InvalidPrefix.verified.txt | 9 +- ...GeneratorTests.InvalidTargets.verified.txt | 17 +++- ...rTests.ParameterAccessibility.verified.txt | 9 +- .../DataPortalOperationsTestHelper.cs | 65 +++++++++++--- ...rTests.AmbiguousOperationName.verified.txt | 9 +- ...rTests.DuplicateOperationName.verified.txt | 17 +++- .../TestAnalyzerConfigOptionsProvider.cs | 32 +++++++ ...UseGeneratedDataPortalExtensionAnalyzer.md | 2 +- releasenotes.md | 7 ++ 29 files changed, 731 insertions(+), 126 deletions(-) create mode 100644 Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsAnalyzer.cs create mode 100644 Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsAnalyzer.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.AsyncOnly#TestApp.PersonEdit.DataPortalExtensions.g.verified.cs create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.AsyncOnly.verified.txt create mode 100644 Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/TestAnalyzerConfigOptionsProvider.cs diff --git a/Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzer.cs b/Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzer.cs index 463caccc18..b5eeb9e4ef 100644 --- a/Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzer.cs +++ b/Source/Csla.Analyzers/Csla.Analyzers/UseGeneratedDataPortalExtensionAnalyzer.cs @@ -56,6 +56,7 @@ public override void Initialize(AnalysisContext context) } var symbols = new KnownSymbols( + GenerateSync(compilationContext.Options.AnalyzerConfigOptionsProvider.GlobalOptions), extensionsAttribute, compilation.GetTypeByMetadataName("Csla.IDataPortal`1"), compilation.GetTypeByMetadataName("Csla.IChildDataPortal`1"), @@ -185,6 +186,11 @@ private static bool HasGeneratedExtension(INamedTypeSymbol businessType, Attribu } var isAsync = portalMethodName.EndsWith("Async", StringComparison.Ordinal); + if (!isAsync && !symbols.GenerateSync) + { + return false; + } + var kind = isAsync ? portalMethodName.Substring(0, portalMethodName.Length - "Async".Length) : portalMethodName; var receiver = kind is "CreateChild" or "FetchChild" ? symbols.IChildDataPortal : symbols.IDataPortal; var hiddenNames = receiver is null @@ -405,7 +411,18 @@ private static Location GetLocation(IInvocationOperation invocation) return invocation.Syntax.GetLocation(); } + /// + /// Mirrors the generator: synchronous extension methods are generated unless + /// the CslaGenerateSyncDataPortalExtensions MSBuild property is false. + /// + private static bool GenerateSync(AnalyzerConfigOptions options) + { + return !(options.TryGetValue("build_property.CslaGenerateSyncDataPortalExtensions", out var value) && + string.Equals(value?.Trim(), "false", StringComparison.OrdinalIgnoreCase)); + } + private sealed class KnownSymbols( + bool generateSync, INamedTypeSymbol dataPortalExtensionsAttribute, INamedTypeSymbol? iDataPortal, INamedTypeSymbol? iChildDataPortal, @@ -414,6 +431,7 @@ private sealed class KnownSymbols( INamedTypeSymbol? injectAttribute, INamedTypeSymbol? iCslaObject) { + public bool GenerateSync { get; } = generateSync; public INamedTypeSymbol DataPortalExtensionsAttribute { get; } = dataPortalExtensionsAttribute; public INamedTypeSymbol? IDataPortal { get; } = iDataPortal; public INamedTypeSymbol? IChildDataPortal { get; } = iChildDataPortal; diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsAnalyzer.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsAnalyzer.cs new file mode 100644 index 0000000000..cc9e5c164d --- /dev/null +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsAnalyzer.cs @@ -0,0 +1,77 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// Reports data portal extension methods that are not generated +//----------------------------------------------------------------------- + +using System.Collections.Immutable; +using Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations; +using Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations.Models; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalExtensions +{ + /// + /// Reports business types marked with [DataPortalExtensions] for which + /// does not generate some + /// or all extension methods, and why. + /// + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class DataPortalExtensionsAnalyzer : DiagnosticAnalyzer + { + /// + public override ImmutableArray SupportedDiagnostics { get; } = + [ + DataPortalOperationsDiagnostics.GenericExtensionsNotGenerated, + DataPortalOperationsDiagnostics.InvalidExtensionsTarget, + DataPortalOperationsDiagnostics.ExtensionNameHidden, + DataPortalOperationsDiagnostics.InaccessibleParameterType, + DataPortalOperationsDiagnostics.DuplicateExtensionMethod, + DataPortalOperationsDiagnostics.InvalidExtensionPrefix + ]; + + /// + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterCompilationStartAction(compilationContext => + { + var compilation = compilationContext.Compilation; + if (compilation.GetTypeByMetadataName(IncrementalDataPortalExtensionsGenerator.DataPortalExtensionsAttributeName) is not { } extensionsAttribute) + return; + + var attributeKinds = DataPortalOperationsAnalyzer.GetOperationAttributeKinds(compilation); + var generateSync = IncrementalDataPortalExtensionsGenerator.GetGenerateSync(compilationContext.Options.AnalyzerConfigOptionsProvider.GlobalOptions); + + compilationContext.RegisterSymbolAction( + symbolContext => AnalyzeType(symbolContext, extensionsAttribute, attributeKinds, generateSync), SymbolKind.NamedType); + }); + } + + private static void AnalyzeType(SymbolAnalysisContext context, INamedTypeSymbol extensionsAttribute, + Dictionary attributeKinds, bool generateSync) + { + var type = (INamedTypeSymbol)context.Symbol; + if (type.TypeKind != TypeKind.Class || type.IsRecord) + return; + + var attribute = type.GetAttributes().FirstOrDefault(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, extensionsAttribute)); + if (attribute is null) + return; + + var compilation = context.Compilation; + var extensionType = IncrementalDataPortalExtensionsGenerator.CreateExtensionType(type, attribute, compilation); + var operationType = OperationDiscovery.GetOperationType(type, attributeKinds, compilation, context.CancellationToken); + var methods = IncrementalDataPortalExtensionsGenerator.GetExtensionMethods( + operationType is null ? [] : [operationType]); + + var (_, diagnostics) = DataPortalExtensionsBuilder.Analyze(new ExtensionGenerationModel(extensionType, methods, generateSync)); + foreach (var diagnostic in diagnostics) + context.ReportDiagnostic(diagnostic.ToDiagnostic(compilation)); + } + } +} diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsBuilder.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsBuilder.cs index f863f6ffcc..7aa1c62a7f 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsBuilder.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/DataPortalExtensionsBuilder.cs @@ -27,42 +27,15 @@ internal static class DataPortalExtensionsBuilder private const string InvokerName = "__invoker"; /// - /// Build the generated source and diagnostics for a business type. - /// Returns a null source when nothing is generated. + /// Build the generated source for a business type. + /// Returns null when nothing is generated. /// - public static (string? Source, List Diagnostics) Build(ExtensionGenerationModel model) + public static string? Build(ExtensionGenerationModel model) { var type = model.Type; - var diagnostics = new List(); - - if (type.IsGeneric) - { - diagnostics.Add(Diagnostic(DataPortalOperationsDiagnostics.GenericExtensionsNotGeneratedId, type.Location, type.TypeName)); - return (null, diagnostics); - } - - var invalidReason = type switch - { - { IsCslaObject: false } => "the type does not implement Csla.Core.ICslaObject", - { IsAbstract: true } => "the type is abstract", - { Visibility: TypeVisibility.Private } => "the type is not accessible outside its containing type", - _ => null - }; - if (invalidReason is not null) - { - diagnostics.Add(Diagnostic(DataPortalOperationsDiagnostics.InvalidExtensionsTargetId, type.Location, type.TypeName, invalidReason)); - return (null, diagnostics); - } - - if (type.Prefix.Length > 0 && !SyntaxFacts.IsValidIdentifier(type.Prefix)) - { - diagnostics.Add(Diagnostic(DataPortalOperationsDiagnostics.InvalidExtensionPrefixId, type.Location, type.TypeName, type.Prefix)); - return (null, diagnostics); - } - - var methods = SelectMethods(model, diagnostics); + var (methods, _) = Analyze(model); if (methods.Count == 0) - return (null, diagnostics); + return null; using var stringWriter = new StringWriter(); var writer = new IndentedTextWriter(stringWriter, " "); @@ -102,7 +75,47 @@ public static (string? Source, List Diagnostics) Build(Extension if (type.Namespace.Length > 0) CloseBlock(writer); - return (stringWriter.ToString(), diagnostics); + return stringWriter.ToString(); + } + + /// + /// Selects the extension methods generated for a business type, and + /// explains, as diagnostics, why any are not generated. The generator + /// uses the methods and the analyzer reports the diagnostics, so both + /// apply the same rules. + /// + public static (List Methods, List Diagnostics) Analyze(ExtensionGenerationModel model) + { + var type = model.Type; + var diagnostics = new List(); + var none = new List(); + + if (type.IsGeneric) + { + diagnostics.Add(Diagnostic(DataPortalOperationsDiagnostics.GenericExtensionsNotGeneratedId, type.Location, type.TypeName)); + return (none, diagnostics); + } + + var invalidReason = type switch + { + { IsCslaObject: false } => "the type does not implement Csla.Core.ICslaObject", + { IsAbstract: true } => "the type is abstract", + { Visibility: TypeVisibility.Private } => "the type is not accessible outside its containing type", + _ => null + }; + if (invalidReason is not null) + { + diagnostics.Add(Diagnostic(DataPortalOperationsDiagnostics.InvalidExtensionsTargetId, type.Location, type.TypeName, invalidReason)); + return (none, diagnostics); + } + + if (type.Prefix.Length > 0 && !SyntaxFacts.IsValidIdentifier(type.Prefix)) + { + diagnostics.Add(Diagnostic(DataPortalOperationsDiagnostics.InvalidExtensionPrefixId, type.Location, type.TypeName, type.Prefix)); + return (none, diagnostics); + } + + return (SelectMethods(model, diagnostics), diagnostics); } private static List SelectMethods(ExtensionGenerationModel model, List diagnostics) @@ -119,7 +132,7 @@ private static List SelectMethods(ExtensionGenerationModel mode // Methods sharing an operation name differ only in injected parameters, // so they would produce identical extension methods; the runtime's - // choice is reported by the operations generator. + // choice is reported by the operations analyzer. if (collisions.Any(c => ReferenceEquals(c.Loser, method))) continue; @@ -140,7 +153,7 @@ private static List SelectMethods(ExtensionGenerationModel mode var hiddenNames = isChild ? type.ChildHiddenNames : type.RootHiddenNames; var parameterTypes = string.Join(",", criteria.Select(p => p.PatternTypeDisplay + (p.IsNullableValueType ? "?" : string.Empty))); - foreach (var isAsync in new[] { true, false }) + foreach (var isAsync in model.GenerateSync ? new[] { true, false } : new[] { true }) { var name = type.Prefix + baseName + (isAsync ? "Async" : string.Empty); if (hiddenNames.Contains(name)) @@ -263,6 +276,9 @@ private static void CloseBlock(IndentedTextWriter writer) writer.WriteLine("}"); } - private sealed record ExtensionMethod(OperationMethodModel Method, string Name, bool IsAsync, bool IsChild); + /// + /// A generated extension method for an operation method. + /// + internal sealed record ExtensionMethod(OperationMethodModel Method, string Name, bool IsAsync, bool IsChild); } } diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/ExtensionTypeModel.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/ExtensionTypeModel.cs index 56a0b27da9..e65e301a4a 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/ExtensionTypeModel.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/ExtensionTypeModel.cs @@ -62,6 +62,7 @@ internal sealed record ExtensionTypeModel /// /// A business type marked with [DataPortalExtensions] and its operation methods. + /// When is false only async extension methods are generated. /// - internal sealed record ExtensionGenerationModel(ExtensionTypeModel Type, EquatableArray Methods); + internal sealed record ExtensionGenerationModel(ExtensionTypeModel Type, EquatableArray Methods, bool GenerateSync); } diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/IncrementalDataPortalExtensionsGenerator.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/IncrementalDataPortalExtensionsGenerator.cs index 6c5755ccb2..3a102fbefa 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/IncrementalDataPortalExtensionsGenerator.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalExtensions/IncrementalDataPortalExtensionsGenerator.cs @@ -15,6 +15,7 @@ using Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations.Models; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalExtensions @@ -23,12 +24,19 @@ namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalExtensions /// Incremental source generator that emits strongly typed extension methods /// on IDataPortal<T> and IChildDataPortal<T> for each /// data portal operation method of a business class marked with - /// [DataPortalExtensions]. + /// [DataPortalExtensions]. Diagnostics about the generated methods + /// are reported by . /// [Generator(LanguageNames.CSharp)] public class IncrementalDataPortalExtensionsGenerator : IIncrementalGenerator { - private const string DataPortalExtensionsAttributeName = "Csla.DataPortalExtensionsAttribute"; + internal const string DataPortalExtensionsAttributeName = "Csla.DataPortalExtensionsAttribute"; + + /// + /// MSBuild property that controls whether synchronous extension methods + /// are generated. Set it to false to generate only the async methods. + /// + internal const string GenerateSyncProperty = "build_property.CslaGenerateSyncDataPortalExtensions"; /// /// Operation kinds that have a client-side data portal method. @@ -48,27 +56,43 @@ public void Initialize(IncrementalGeneratorInitializationContext context) var operationTypes = IncrementalDataPortalOperationsGenerator.GetOperationTypes(context); + var generateSync = context.AnalyzerConfigOptionsProvider + .Select(static (provider, _) => GetGenerateSync(provider.GlobalOptions)) + .WithTrackingName(TrackingNames.ExtensionOptions); + var models = extensionTypes .Combine(operationTypes) + .Combine(generateSync) .Select(static (pair, _) => new ExtensionGenerationModel( - pair.Left, - new EquatableArray( - pair.Right - .Where(t => t.Type.MetadataName == pair.Left.MetadataName) - .SelectMany(t => t.Methods) - .Where(m => SupportedKinds.Contains(m.Kind))))) + pair.Left.Left, + GetExtensionMethods(pair.Left.Right.Where(t => t.Type.MetadataName == pair.Left.Left.MetadataName)), + pair.Right)) .WithTrackingName(TrackingNames.ExtensionTypes); context.RegisterSourceOutput(models, static (spc, model) => { - var (source, diagnostics) = DataPortalExtensionsBuilder.Build(model); - foreach (var diagnostic in diagnostics) - spc.ReportDiagnostic(diagnostic.ToDiagnostic()); + var source = DataPortalExtensionsBuilder.Build(model); if (source is not null) spc.AddSource($"{model.Type.HintName}.DataPortalExtensions.g.cs", SourceText.From(source, Encoding.UTF8)); }); } + /// + /// Whether synchronous extension methods are generated. They are unless + /// the CslaGenerateSyncDataPortalExtensions MSBuild property is false. + /// + internal static bool GetGenerateSync(AnalyzerConfigOptions options) + => !(options.TryGetValue(GenerateSyncProperty, out var value) + && string.Equals(value?.Trim(), "false", StringComparison.OrdinalIgnoreCase)); + + /// + /// The operation methods of the business type that have a client-side data portal method. + /// + internal static EquatableArray GetExtensionMethods(IEnumerable operationTypes) + => new(operationTypes + .SelectMany(t => t.Methods) + .Where(m => SupportedKinds.Contains(m.Kind))); + private static ExtensionTypeModel? ExtractExtensionType(GeneratorAttributeSyntaxContext context, CancellationToken ct) { if (context.TargetSymbol is not INamedTypeSymbol type) @@ -76,7 +100,14 @@ public void Initialize(IncrementalGeneratorInitializationContext context) ct.ThrowIfCancellationRequested(); - var attribute = context.Attributes.FirstOrDefault(); + return CreateExtensionType(type, context.Attributes.FirstOrDefault(), context.SemanticModel.Compilation); + } + + /// + /// Build the model for a business type marked with [DataPortalExtensions]. + /// + internal static ExtensionTypeModel CreateExtensionType(INamedTypeSymbol type, AttributeData? attribute, Compilation compilation) + { var prefix = attribute?.NamedArguments .Where(a => a.Key == "Prefix") .Select(a => a.Value.Value as string) @@ -93,8 +124,6 @@ public void Initialize(IncrementalGeneratorInitializationContext context) hintParts.AddRange(containers.Select(c => c.Name)); hintParts.Add(type.Name); - var compilation = context.SemanticModel.Compilation; - return new ExtensionTypeModel { MetadataName = OperationDiscovery.GetFullMetadataName(type), diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsAnalyzer.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsAnalyzer.cs new file mode 100644 index 0000000000..88fbc9954c --- /dev/null +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsAnalyzer.cs @@ -0,0 +1,73 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: https://cslanet.com +// +// Reports data portal operation methods that share an operation name +//----------------------------------------------------------------------- + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations +{ + /// + /// Reports operation methods of a partial class that map to the same + /// data portal operation name, and how + /// dispatches them. + /// + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class DataPortalOperationsAnalyzer : DiagnosticAnalyzer + { + /// + public override ImmutableArray SupportedDiagnostics { get; } = + [DataPortalOperationsDiagnostics.DuplicateOperationName, DataPortalOperationsDiagnostics.AmbiguousOperationName]; + + /// + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterCompilationStartAction(compilationContext => + { + var attributeKinds = GetOperationAttributeKinds(compilationContext.Compilation); + if (attributeKinds.Count == 0) + return; + + compilationContext.RegisterSymbolAction(symbolContext => AnalyzeType(symbolContext, attributeKinds), SymbolKind.NamedType); + }); + } + + /// + /// The operation attribute types in the compilation, mapped to their operation kind. + /// + internal static Dictionary GetOperationAttributeKinds(Compilation compilation) + { + var result = new Dictionary(SymbolEqualityComparer.Default); + foreach (var kind in OperationDiscovery.OperationKinds) + { + if (compilation.GetTypeByMetadataName(OperationDiscovery.GetAttributeMetadataName(kind)) is { } attribute) + result[attribute] = kind; + } + return result; + } + + private static void AnalyzeType(SymbolAnalysisContext context, Dictionary attributeKinds) + { + var type = (INamedTypeSymbol)context.Symbol; + var model = OperationDiscovery.GetOperationType(type, attributeKinds, context.Compilation, context.CancellationToken); + if (model is null || !model.Type.IsPartial) + return; + + var (_, collisions) = DataPortalOperationsBuilder.SelectNamedDispatchMethods(model.Methods); + foreach (var collision in collisions) + { + context.ReportDiagnostic(Diagnostic.Create( + collision.IsAmbiguous ? DataPortalOperationsDiagnostics.AmbiguousOperationName : DataPortalOperationsDiagnostics.DuplicateOperationName, + collision.Loser.Location?.ToLocation(context.Compilation), + collision.Winner.MethodDisplay, collision.Loser.MethodDisplay, model.Type.TypeName, collision.Winner.OperationName)); + } + } + } +} diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs index aca0da279d..fa09d6f32d 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs @@ -3,7 +3,7 @@ // Copyright (c) Marimer LLC. All rights reserved. // Website: https://cslanet.com // -// Diagnostics reported by the data portal generators +// Diagnostics reported by the data portal analyzers //----------------------------------------------------------------------- using Microsoft.CodeAnalysis; @@ -11,8 +11,9 @@ namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations { /// - /// Diagnostic descriptors reported by the data portal operations - /// and data portal extensions generators. + /// Diagnostic descriptors reported by the data portal operations and data + /// portal extensions analyzers, which apply the same rules as the generators. + /// The generators themselves report no diagnostics. /// internal static class DataPortalOperationsDiagnostics { diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/IncrementalDataPortalOperationsGenerator.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/IncrementalDataPortalOperationsGenerator.cs index 2dabcda2cf..1135bf5b2e 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/IncrementalDataPortalOperationsGenerator.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/IncrementalDataPortalOperationsGenerator.cs @@ -33,17 +33,9 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .Where(static t => t.Type.IsPartial) .WithTrackingName(TrackingNames.OperationTypes); + // Diagnostics for these types are reported by DataPortalOperationsAnalyzer. context.RegisterSourceOutput(types, static (spc, model) => { - var (_, collisions) = DataPortalOperationsBuilder.SelectNamedDispatchMethods(model.Methods); - foreach (var collision in collisions) - { - spc.ReportDiagnostic(Diagnostic.Create( - collision.IsAmbiguous ? DataPortalOperationsDiagnostics.AmbiguousOperationName : DataPortalOperationsDiagnostics.DuplicateOperationName, - collision.Loser.Location?.ToLocation(), - collision.Winner.MethodDisplay, collision.Loser.MethodDisplay, model.Type.TypeName, collision.Winner.OperationName)); - } - spc.AddSource( $"{model.Type.HintName}.DataPortalOperations.g.cs", SourceText.From(DataPortalOperationsBuilder.Build(model), Encoding.UTF8)); @@ -74,25 +66,8 @@ internal static IncrementalValueProvider> Get } return all!.Value - .Select(static (entries, ct) => GroupByType(entries, ct)) + .Select(static (entries, ct) => OperationDiscovery.GroupByType(entries, ct)) .WithTrackingName(TrackingNames.GroupOperationTypes); } - - private static EquatableArray GroupByType(ImmutableArray entries, CancellationToken ct) - { - var result = new List(); - foreach (var group in entries.GroupBy(e => e.Type.MetadataName).OrderBy(g => g.Key, StringComparer.Ordinal)) - { - ct.ThrowIfCancellationRequested(); - var header = group.First().Type with { IsPartial = group.All(e => e.Type.IsPartial) }; - var methods = group - .Select(e => e.Method) - .OrderBy(m => m.Location?.FilePath ?? string.Empty, StringComparer.Ordinal) - .ThenBy(m => m.Location?.TextSpan.Start ?? 0) - .ThenBy(m => Array.IndexOf(OperationDiscovery.OperationKinds, m.Kind)); - result.Add(new OperationTypeModel(header, new EquatableArray(methods))); - } - return new EquatableArray(result); - } } } diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/LocationInfo.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/LocationInfo.cs index e429abd486..89a8506be8 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/LocationInfo.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/Models/LocationInfo.cs @@ -17,7 +17,19 @@ namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations.Mod /// internal sealed record LocationInfo(string FilePath, TextSpan TextSpan, LinePositionSpan LineSpan) { - public Location ToLocation() => Location.Create(FilePath, TextSpan, LineSpan); + /// + /// The location in the compilation's syntax tree for the file, so the + /// diagnostic can be suppressed in source; otherwise a file location. + /// + public Location ToLocation(Compilation compilation) + { + foreach (var tree in compilation.SyntaxTrees) + { + if (tree.FilePath == FilePath) + return Location.Create(tree, TextSpan); + } + return Location.Create(FilePath, TextSpan, LineSpan); + } public static LocationInfo? From(Location? location) { @@ -28,14 +40,15 @@ internal sealed record LocationInfo(string FilePath, TextSpan TextSpan, LinePosi } /// - /// An equatable diagnostic to be reported by a generator. + /// An equatable diagnostic about data portal operations or extensions, + /// reported by the analyzers. /// internal sealed record DiagnosticInfo(string Id, LocationInfo? Location, EquatableArray MessageArgs) { - public Diagnostic ToDiagnostic() + public Diagnostic ToDiagnostic(Compilation compilation) { var descriptor = DataPortalOperationsDiagnostics.GetDescriptor(Id); - return Diagnostic.Create(descriptor, Location?.ToLocation(), MessageArgs.Cast().ToArray()); + return Diagnostic.Create(descriptor, Location?.ToLocation(compilation), MessageArgs.Cast().ToArray()); } } } diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs index 6f8db9420c..17a85c0ef5 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/OperationDiscovery.cs @@ -88,6 +88,70 @@ internal static class OperationDiscovery return new OperationMethodEntry(header, model); } + /// + /// Build the model for a type's operation methods from its symbol, as + /// the generator pipeline does from the attributed method declarations. + /// Used by the analyzers. Returns null when the type declares no + /// operation methods. + /// + /// The type to inspect. + /// The operation attribute types, mapped to their operation kind. + /// The compilation containing the type. + /// Cancellation token. + public static OperationTypeModel? GetOperationType(INamedTypeSymbol type, IReadOnlyDictionary attributeKinds, Compilation compilation, CancellationToken ct) + { + if (type.TypeKind != TypeKind.Class) + return null; + + OperationTypeHeader? header = null; + var entries = new List(); + foreach (var method in type.GetMembers().OfType()) + { + if (!IsCandidateMethod(method)) + continue; + + foreach (var attribute in method.GetAttributes()) + { + if (attribute.AttributeClass is null || !attributeKinds.TryGetValue(attribute.AttributeClass, out var kind)) + continue; + + var node = method.DeclaringSyntaxReferences + .Select(r => r.GetSyntax(ct)) + .OfType() + .FirstOrDefault(n => n.AttributeLists.SelectMany(l => l.Attributes).Any()); + if (node is null) + continue; + + ct.ThrowIfCancellationRequested(); + header ??= BuildHeader(type, node, compilation); + entries.Add(new OperationMethodEntry(header with { IsPartial = IsPartialAllTheWay(node) }, BuildMethod(method, kind))); + } + } + + return entries.Count == 0 ? null : GroupByType(entries, ct)[0]; + } + + /// + /// Groups discovered operation methods by type so a partial class + /// spread across several files yields one model. + /// + public static EquatableArray GroupByType(IEnumerable entries, CancellationToken ct) + { + var result = new List(); + foreach (var group in entries.GroupBy(e => e.Type.MetadataName).OrderBy(g => g.Key, StringComparer.Ordinal)) + { + ct.ThrowIfCancellationRequested(); + var header = group.First().Type with { IsPartial = group.All(e => e.Type.IsPartial) }; + var methods = group + .Select(e => e.Method) + .OrderBy(m => m.Location?.FilePath ?? string.Empty, StringComparer.Ordinal) + .ThenBy(m => m.Location?.TextSpan.Start ?? 0) + .ThenBy(m => Array.IndexOf(OperationKinds, m.Kind)); + result.Add(new OperationTypeModel(header, new EquatableArray(methods))); + } + return new EquatableArray(result); + } + /// /// A method that the generator includes in the operations interface. /// diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/TrackingNames.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/TrackingNames.cs index defcac6413..f1928607f9 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/TrackingNames.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/TrackingNames.cs @@ -25,6 +25,9 @@ public static class TrackingNames /// Extraction of types marked with DataPortalExtensions. public const string ExtractExtensionTypes = nameof(ExtractExtensionTypes); + /// Build options for extension generation. + public const string ExtensionOptions = nameof(ExtensionOptions); + /// Per-type models for extension generation. public const string ExtensionTypes = nameof(ExtensionTypes); } diff --git a/Source/Csla/buildTransitive/Csla.targets b/Source/Csla/buildTransitive/Csla.targets index b8a7814a0c..ca179dc3b4 100644 --- a/Source/Csla/buildTransitive/Csla.targets +++ b/Source/Csla/buildTransitive/Csla.targets @@ -1,4 +1,13 @@ + + + + + + false + + none ``` + + With no async suffix, the sync methods would have the same names as the async methods, so they are not generated. +* The `CSLADP002` to `CSLADP011` diagnostics, about operation methods that share an operation name and about extension methods that are not generated, are reported by analyzers rather than by the source generators, so they can be suppressed in source. * Custom or mock `IDataPortal` implementations continue to work; the generated methods fall back to the existing `params object[]` methods. -* Analyzer **CSLA0025** suggests using the generated extension methods instead of calling `IDataPortal` methods with untyped criteria for classes marked `[DataPortalExtensions]`. +* Analyzer **CSLA0025** suggests using the generated extension methods instead of calling `IDataPortal` methods with untyped criteria, and provides a code fix that replaces the call. The data portal extension generator and the CSLA0025 analyzer are adapted from [Csla.DataPortalExtensions](https://github.com/StefanOssendorf/Csla.DataPortalExtensions) by [Stefan Ossendorf](https://github.com/StefanOssendorf), used under the MIT License. Thank you, Stefan! From b8e599438741b3459ec9538bcc5f5b55074cdfcc Mon Sep 17 00:00:00 2001 From: Rockford lhotka Date: Thu, 8 Oct 2026 15:43:46 -0500 Subject: [PATCH 10/10] #4359 Address Stefan's second review: Linux pack, CSLADP docs, DI calls - Fix dotnet pack on Linux: Csla.csproj packed Csla.Analyzers.dll from a hard-coded bin\Release path, but the analyzers build to Bin\. Both the analyzers and the bundled generator are now packed from GetTargetPath. - Generated dispatch resolves [Inject] parameters by calling the Microsoft.Extensions.DependencyInjection methods directly; remove DataPortalOperationHelper.GetService and GetKeyedService. - Remove the nullable accumulator in GetOperationTypes and add a test for business objects without operation methods. - Document CSLADP002-CSLADP011, add help links to the descriptors, and add pages for the data portal operations and extensions generators. - Release notes: clarify the sync extensions property and drop the partial base classes item from breaking changes. Co-Authored-By: Claude Opus 5.5 --- .../DataPortalOperationsBuilder.cs | 13 ++- .../DataPortalOperationsDiagnostics.cs | 29 +++-- ...ncrementalDataPortalOperationsGenerator.cs | 32 +++--- Source/Csla/Csla.csproj | 19 ++-- .../Csla/Server/DataPortalOperationHelper.cs | 46 -------- ...ssemblyAttributeInvalidPrefix.verified.txt | 1 + ...sionsGeneratorTests.AsyncOnly.verified.txt | 2 + ...ratorTests.DuplicateExtension.verified.txt | 2 + ...ratorTests.GenericTypeSkipped.verified.txt | 1 + ...onsGeneratorTests.HiddenNames.verified.txt | 4 + ...ratorTests.InvalidAsyncSuffix.verified.txt | 1 + ...sGeneratorTests.InvalidPrefix.verified.txt | 1 + ...GeneratorTests.InvalidTargets.verified.txt | 2 + ...orTests.NoAsyncSuffixWithSync.verified.txt | 1 + ...rTests.ParameterAccessibility.verified.txt | 1 + .../DataPortalOperationsGeneratorTests.cs | 22 ++++ .../DataPortalOperationsTestHelper.cs | 4 +- ...rTests.AmbiguousOperationName.verified.txt | 1 + ...ineItem.DataPortalOperations.g.verified.cs | 12 +-- ...sonEdit.DataPortalOperations.g.verified.cs | 12 +-- ...sonEdit.DataPortalOperations.g.verified.cs | 4 +- ...rTests.DuplicateOperationName.verified.txt | 2 + ...sonEdit.DataPortalOperations.g.verified.cs | 8 +- ...sonEdit.DataPortalOperations.g.verified.cs | 8 +- ...sonEdit.DataPortalOperations.g.verified.cs | 16 +-- ...sonEdit.DataPortalOperations.g.verified.cs | 4 +- ...sonEdit.DataPortalOperations.g.verified.cs | 4 +- .../DataPortal/DataPortalExtensionsTests.cs | 4 - .../CSLADP002-DuplicateOperationName.md | 37 +++++++ ...CSLADP003-GenericExtensionsNotGenerated.md | 33 ++++++ .../CSLADP004-InvalidExtensionsTarget.md | 33 ++++++ .../CSLADP005-ExtensionNameHidden.md | 35 ++++++ .../CSLADP006-InaccessibleParameterType.md | 31 ++++++ .../CSLADP007-DuplicateExtensionMethod.md | 28 +++++ .../CSLADP008-InvalidExtensionPrefix.md | 25 +++++ .../CSLADP009-AmbiguousOperationName.md | 31 ++++++ .../analyzers/CSLADP010-InvalidAsyncSuffix.md | 25 +++++ .../CSLADP011-SyncExtensionsNotGenerated.md | 26 +++++ docs/analyzers/index.md | 14 +++ docs/code-generators.md | 4 +- docs/data-portal-extensions-generator.md | 101 ++++++++++++++++++ docs/data-portal-operations-generator.md | 57 ++++++++++ releasenotes.md | 7 +- 43 files changed, 616 insertions(+), 127 deletions(-) create mode 100644 docs/analyzers/CSLADP002-DuplicateOperationName.md create mode 100644 docs/analyzers/CSLADP003-GenericExtensionsNotGenerated.md create mode 100644 docs/analyzers/CSLADP004-InvalidExtensionsTarget.md create mode 100644 docs/analyzers/CSLADP005-ExtensionNameHidden.md create mode 100644 docs/analyzers/CSLADP006-InaccessibleParameterType.md create mode 100644 docs/analyzers/CSLADP007-DuplicateExtensionMethod.md create mode 100644 docs/analyzers/CSLADP008-InvalidExtensionPrefix.md create mode 100644 docs/analyzers/CSLADP009-AmbiguousOperationName.md create mode 100644 docs/analyzers/CSLADP010-InvalidAsyncSuffix.md create mode 100644 docs/analyzers/CSLADP011-SyncExtensionsNotGenerated.md create mode 100644 docs/data-portal-extensions-generator.md create mode 100644 docs/data-portal-operations-generator.md diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsBuilder.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsBuilder.cs index 90ba4ae6b7..ccff75a627 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsBuilder.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsBuilder.cs @@ -20,6 +20,8 @@ namespace Csla.Generator.AutoImplementProperties.CSharp.DataPortalOperations internal static class DataPortalOperationsBuilder { private const string HelperType = "global::Csla.Server.DataPortalOperationHelper"; + private const string ServiceProviderExtensionsType = "global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions"; + private const string KeyedServiceProviderExtensionsType = "global::Microsoft.Extensions.DependencyInjection.ServiceProviderKeyedServiceExtensions"; private const string OperationsLocal = "__operations"; private const string ExceptionLocal = "__ex"; @@ -350,9 +352,14 @@ private static string FormatServiceResolution(OperationParameterModel parameter) var serviceType = parameter.IsNullableValueType ? $"typeof({parameter.PatternTypeDisplay}?)" : $"typeof({parameter.PatternTypeDisplay})"; - var resolution = parameter.ServiceKeyExpression is null - ? $"{HelperType}.GetService(serviceProvider, {serviceType}, {(parameter.AllowNull ? "true" : "false")})" - : $"{HelperType}.GetKeyedService(serviceProvider, {serviceType}, {parameter.ServiceKeyExpression}, {(parameter.AllowNull ? "true" : "false")})"; + // Same resolution calls as reflection-based dispatch. + var resolution = (parameter.ServiceKeyExpression, parameter.AllowNull) switch + { + (null, true) => $"serviceProvider.GetService({serviceType})", + (null, false) => $"{ServiceProviderExtensionsType}.GetRequiredService(serviceProvider, {serviceType})", + (var key, true) => $"{KeyedServiceProviderExtensionsType}.GetKeyedService(serviceProvider, {serviceType}, {key})", + (var key, false) => $"{KeyedServiceProviderExtensionsType}.GetRequiredKeyedService(serviceProvider, {serviceType}, {key})", + }; return parameter.AllowNull ? $"({parameter.TypeDisplay})({resolution})" : $"({parameter.TypeDisplay})({resolution})!"; diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs index c36720a181..2f4befba85 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/DataPortalOperationsDiagnostics.cs @@ -19,6 +19,9 @@ internal static class DataPortalOperationsDiagnostics { private const string Category = "Csla.DataPortal"; + private static string HelpLink(string id, string name) => + $"https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/{id}-{name}.md"; + public const string DuplicateOperationNameId = "CSLADP002"; public const string GenericExtensionsNotGeneratedId = "CSLADP003"; public const string InvalidExtensionsTargetId = "CSLADP004"; @@ -36,7 +39,8 @@ internal static class DataPortalOperationsDiagnostics messageFormat: "Methods '{0}' and '{1}' on '{2}' both map to data portal operation '{3}'; name-based dispatch uses '{0}'", category: Category, defaultSeverity: DiagnosticSeverity.Info, - isEnabledByDefault: true); + isEnabledByDefault: true, + helpLinkUri: HelpLink(DuplicateOperationNameId, nameof(DuplicateOperationName))); public static readonly DiagnosticDescriptor GenericExtensionsNotGenerated = new( id: GenericExtensionsNotGeneratedId, @@ -44,7 +48,8 @@ internal static class DataPortalOperationsDiagnostics messageFormat: "Data portal extension methods are not generated for '{0}' because it is generic or nested in a generic type", category: Category, defaultSeverity: DiagnosticSeverity.Info, - isEnabledByDefault: true); + isEnabledByDefault: true, + helpLinkUri: HelpLink(GenericExtensionsNotGeneratedId, nameof(GenericExtensionsNotGenerated))); public static readonly DiagnosticDescriptor InvalidExtensionsTarget = new( id: InvalidExtensionsTargetId, @@ -52,7 +57,8 @@ internal static class DataPortalOperationsDiagnostics messageFormat: "Data portal extension methods are not generated for '{0}': {1}", category: Category, defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true); + isEnabledByDefault: true, + helpLinkUri: HelpLink(InvalidExtensionsTargetId, nameof(InvalidExtensionsTarget))); public static readonly DiagnosticDescriptor ExtensionNameHidden = new( id: ExtensionNameHiddenId, @@ -60,7 +66,8 @@ internal static class DataPortalOperationsDiagnostics messageFormat: "The data portal extension method '{0}' for '{1}' is not generated because it would be hidden by the '{2}' instance method; set DataPortalExtensions.Prefix to give the generated methods distinct names", category: Category, defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true); + isEnabledByDefault: true, + helpLinkUri: HelpLink(ExtensionNameHiddenId, nameof(ExtensionNameHidden))); public static readonly DiagnosticDescriptor InaccessibleParameterType = new( id: InaccessibleParameterTypeId, @@ -68,7 +75,8 @@ internal static class DataPortalOperationsDiagnostics messageFormat: "The data portal extension method for '{0}' cannot be generated because parameter '{1}' has type '{2}', which is not accessible outside its containing type", category: Category, defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true); + isEnabledByDefault: true, + helpLinkUri: HelpLink(InaccessibleParameterTypeId, nameof(InaccessibleParameterType))); public static readonly DiagnosticDescriptor DuplicateExtensionMethod = new( id: DuplicateExtensionMethodId, @@ -76,7 +84,8 @@ internal static class DataPortalOperationsDiagnostics messageFormat: "The data portal extension method '{0}' for '{1}' is not generated for '{2}' because another operation method already produces an extension method with the same signature", category: Category, defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true); + isEnabledByDefault: true, + helpLinkUri: HelpLink(DuplicateExtensionMethodId, nameof(DuplicateExtensionMethod))); public static readonly DiagnosticDescriptor InvalidExtensionPrefix = new( id: InvalidExtensionPrefixId, @@ -84,7 +93,8 @@ internal static class DataPortalOperationsDiagnostics messageFormat: "Data portal extension methods are not generated for '{0}' because the prefix '{1}' is not a valid C# identifier", category: Category, defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true); + isEnabledByDefault: true, + helpLinkUri: HelpLink(InvalidExtensionPrefixId, nameof(InvalidExtensionPrefix))); public static readonly DiagnosticDescriptor AmbiguousOperationName = new( id: AmbiguousOperationNameId, @@ -92,7 +102,8 @@ internal static class DataPortalOperationsDiagnostics messageFormat: "Methods '{0}' and '{1}' on '{2}' both map to data portal operation '{3}' with the same number of injected parameters; the data portal cannot choose between them and reports an ambiguous match", category: Category, defaultSeverity: DiagnosticSeverity.Warning, - isEnabledByDefault: true); + isEnabledByDefault: true, + helpLinkUri: HelpLink(AmbiguousOperationNameId, nameof(AmbiguousOperationName))); public static readonly DiagnosticDescriptor InvalidAsyncSuffix = new( id: InvalidAsyncSuffixId, @@ -101,6 +112,7 @@ internal static class DataPortalOperationsDiagnostics category: Category, defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true, + helpLinkUri: HelpLink(InvalidAsyncSuffixId, nameof(InvalidAsyncSuffix)), customTags: WellKnownDiagnosticTags.CompilationEnd); public static readonly DiagnosticDescriptor SyncExtensionsNotGenerated = new( @@ -110,6 +122,7 @@ internal static class DataPortalOperationsDiagnostics category: Category, defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true, + helpLinkUri: HelpLink(SyncExtensionsNotGeneratedId, nameof(SyncExtensionsNotGenerated)), customTags: WellKnownDiagnosticTags.CompilationEnd); } } diff --git a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/IncrementalDataPortalOperationsGenerator.cs b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/IncrementalDataPortalOperationsGenerator.cs index 1135bf5b2e..9a7cf5be9d 100644 --- a/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/IncrementalDataPortalOperationsGenerator.cs +++ b/Source/Csla.Generators/cs/AutoImplementProperties/Csla.Generator.AutoImplementProperties.CSharp/DataPortalOperations/IncrementalDataPortalOperationsGenerator.cs @@ -48,26 +48,28 @@ public void Initialize(IncrementalGeneratorInitializationContext context) /// internal static IncrementalValueProvider> GetOperationTypes(IncrementalGeneratorInitializationContext context) { - IncrementalValueProvider>? all = null; - foreach (var kind in OperationDiscovery.OperationKinds) + // A compilation without operation methods yields an empty array, not null. + var kinds = OperationDiscovery.OperationKinds; + var all = GetOperationMethods(context, kinds[0]); + for (var i = 1; i < kinds.Length; i++) { - var entries = context.SyntaxProvider.ForAttributeWithMetadataName( - OperationDiscovery.GetAttributeMetadataName(kind), - predicate: static (node, _) => node is MethodDeclarationSyntax, - transform: (ctx, ct) => OperationDiscovery.ExtractMethod(ctx, kind, ct)) - .Where(static e => e is not null) - .Select(static (e, _) => e!) - .WithTrackingName(TrackingNames.ExtractOperationMethods) - .Collect(); - - all = all is null - ? entries - : all.Value.Combine(entries).Select(static (pair, _) => pair.Left.AddRange(pair.Right)); + all = all.Combine(GetOperationMethods(context, kinds[i])) + .Select(static (pair, _) => pair.Left.AddRange(pair.Right)); } - return all!.Value + return all .Select(static (entries, ct) => OperationDiscovery.GroupByType(entries, ct)) .WithTrackingName(TrackingNames.GroupOperationTypes); } + + private static IncrementalValueProvider> GetOperationMethods(IncrementalGeneratorInitializationContext context, string kind) + => context.SyntaxProvider.ForAttributeWithMetadataName( + OperationDiscovery.GetAttributeMetadataName(kind), + predicate: static (node, _) => node is MethodDeclarationSyntax, + transform: (ctx, ct) => OperationDiscovery.ExtractMethod(ctx, kind, ct)) + .Where(static e => e is not null) + .Select(static (e, _) => e!) + .WithTrackingName(TrackingNames.ExtractOperationMethods) + .Collect(); } } diff --git a/Source/Csla/Csla.csproj b/Source/Csla/Csla.csproj index 1ecfe6f1cd..a64c6a841c 100644 --- a/Source/Csla/Csla.csproj +++ b/Source/Csla/Csla.csproj @@ -109,10 +109,8 @@ - - true - analyzers/dotnet/cs/ - + + @@ -129,17 +127,18 @@ - + - $(TargetsForTfmSpecificContentInPackage);CslaPackBundledGenerators + $(TargetsForTfmSpecificContentInPackage);CslaPackBundledAnalyzers - - - + + + - + diff --git a/Source/Csla/Server/DataPortalOperationHelper.cs b/Source/Csla/Server/DataPortalOperationHelper.cs index cb89f974e7..fd48567787 100644 --- a/Source/Csla/Server/DataPortalOperationHelper.cs +++ b/Source/Csla/Server/DataPortalOperationHelper.cs @@ -65,51 +65,5 @@ public static Exception CreateCallMethodException(object target, string methodNa return new CallMethodException(target.GetType().Name + "." + methodName + " " + Resources.MethodCallFailed, exception); } - - /// - /// Resolves a service for an injected operation method parameter. - /// - /// Current service provider. - /// Type of service. - /// True if the parameter allows a null value. - /// or is . - /// The service is required and not registered. - public static object? GetService(IServiceProvider serviceProvider, Type serviceType, bool allowNull) - { - if (serviceProvider is null) - throw new ArgumentNullException(nameof(serviceProvider)); - if (serviceType is null) - throw new ArgumentNullException(nameof(serviceType)); - - return allowNull - ? serviceProvider.GetService(serviceType) - : serviceProvider.GetRequiredService(serviceType); - } - - /// - /// Resolves a keyed service for an injected operation method parameter. - /// - /// Current service provider. - /// Type of service. - /// Service key. - /// True if the parameter allows a null value. - /// or is . - /// The service is required and not registered, or the provider does not support keyed services. - public static object? GetKeyedService(IServiceProvider serviceProvider, Type serviceType, object? serviceKey, bool allowNull) - { - if (serviceProvider is null) - throw new ArgumentNullException(nameof(serviceProvider)); - if (serviceType is null) - throw new ArgumentNullException(nameof(serviceType)); - - if (allowNull) - { - if (serviceProvider is IKeyedServiceProvider keyedProvider) - return keyedProvider.GetKeyedService(serviceType, serviceKey); - throw new InvalidOperationException("Service provider must implement IKeyedServiceProvider to support keyed services."); - } - - return serviceProvider.GetRequiredKeyedService(serviceType, serviceKey); - } } } diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.AssemblyAttributeInvalidPrefix.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.AssemblyAttributeInvalidPrefix.verified.txt index 02a365f226..851c320f2e 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.AssemblyAttributeInvalidPrefix.verified.txt +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.AssemblyAttributeInvalidPrefix.verified.txt @@ -15,6 +15,7 @@ Descriptor: { Id: CSLADP008, Title: Invalid DataPortalExtensions prefix, + HelpLink: https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/CSLADP008-InvalidExtensionPrefix.md, MessageFormat: Data portal extension methods are not generated for '{0}' because the prefix '{1}' is not a valid C# identifier, Category: Csla.DataPortal, DefaultSeverity: Warning, diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.AsyncOnly.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.AsyncOnly.verified.txt index 6641ab7e54..3a49ff4fe0 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.AsyncOnly.verified.txt +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.AsyncOnly.verified.txt @@ -15,6 +15,7 @@ Descriptor: { Id: CSLADP005, Title: Generated data portal extension would be hidden, + HelpLink: https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/CSLADP005-ExtensionNameHidden.md, MessageFormat: The data portal extension method '{0}' for '{1}' is not generated because it would be hidden by the '{2}' instance method; set DataPortalExtensions.Prefix to give the generated methods distinct names, Category: Csla.DataPortal, DefaultSeverity: Warning, @@ -35,6 +36,7 @@ Descriptor: { Id: CSLADP005, Title: Generated data portal extension would be hidden, + HelpLink: https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/CSLADP005-ExtensionNameHidden.md, MessageFormat: The data portal extension method '{0}' for '{1}' is not generated because it would be hidden by the '{2}' instance method; set DataPortalExtensions.Prefix to give the generated methods distinct names, Category: Csla.DataPortal, DefaultSeverity: Warning, diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.DuplicateExtension.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.DuplicateExtension.verified.txt index b39341c196..3168a54f5b 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.DuplicateExtension.verified.txt +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.DuplicateExtension.verified.txt @@ -15,6 +15,7 @@ Descriptor: { Id: CSLADP007, Title: Generated data portal extension would be duplicated, + HelpLink: https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/CSLADP007-DuplicateExtensionMethod.md, MessageFormat: The data portal extension method '{0}' for '{1}' is not generated for '{2}' because another operation method already produces an extension method with the same signature, Category: Csla.DataPortal, DefaultSeverity: Warning, @@ -35,6 +36,7 @@ Descriptor: { Id: CSLADP007, Title: Generated data portal extension would be duplicated, + HelpLink: https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/CSLADP007-DuplicateExtensionMethod.md, MessageFormat: The data portal extension method '{0}' for '{1}' is not generated for '{2}' because another operation method already produces an extension method with the same signature, Category: Csla.DataPortal, DefaultSeverity: Warning, diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.GenericTypeSkipped.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.GenericTypeSkipped.verified.txt index a208114433..436398fb14 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.GenericTypeSkipped.verified.txt +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.GenericTypeSkipped.verified.txt @@ -15,6 +15,7 @@ Descriptor: { Id: CSLADP003, Title: Data portal extensions are not generated for generic types, + HelpLink: https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/CSLADP003-GenericExtensionsNotGenerated.md, MessageFormat: Data portal extension methods are not generated for '{0}' because it is generic or nested in a generic type, Category: Csla.DataPortal, DefaultSeverity: Info, diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.HiddenNames.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.HiddenNames.verified.txt index b7b47deb4b..2be6bce04f 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.HiddenNames.verified.txt +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.HiddenNames.verified.txt @@ -15,6 +15,7 @@ Descriptor: { Id: CSLADP005, Title: Generated data portal extension would be hidden, + HelpLink: https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/CSLADP005-ExtensionNameHidden.md, MessageFormat: The data portal extension method '{0}' for '{1}' is not generated because it would be hidden by the '{2}' instance method; set DataPortalExtensions.Prefix to give the generated methods distinct names, Category: Csla.DataPortal, DefaultSeverity: Warning, @@ -35,6 +36,7 @@ Descriptor: { Id: CSLADP005, Title: Generated data portal extension would be hidden, + HelpLink: https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/CSLADP005-ExtensionNameHidden.md, MessageFormat: The data portal extension method '{0}' for '{1}' is not generated because it would be hidden by the '{2}' instance method; set DataPortalExtensions.Prefix to give the generated methods distinct names, Category: Csla.DataPortal, DefaultSeverity: Warning, @@ -55,6 +57,7 @@ Descriptor: { Id: CSLADP005, Title: Generated data portal extension would be hidden, + HelpLink: https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/CSLADP005-ExtensionNameHidden.md, MessageFormat: The data portal extension method '{0}' for '{1}' is not generated because it would be hidden by the '{2}' instance method; set DataPortalExtensions.Prefix to give the generated methods distinct names, Category: Csla.DataPortal, DefaultSeverity: Warning, @@ -75,6 +78,7 @@ Descriptor: { Id: CSLADP005, Title: Generated data portal extension would be hidden, + HelpLink: https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/CSLADP005-ExtensionNameHidden.md, MessageFormat: The data portal extension method '{0}' for '{1}' is not generated because it would be hidden by the '{2}' instance method; set DataPortalExtensions.Prefix to give the generated methods distinct names, Category: Csla.DataPortal, DefaultSeverity: Warning, diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidAsyncSuffix.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidAsyncSuffix.verified.txt index f0f683a2a0..42ee9b8d12 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidAsyncSuffix.verified.txt +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidAsyncSuffix.verified.txt @@ -8,6 +8,7 @@ Descriptor: { Id: CSLADP010, Title: Invalid data portal extensions async suffix, + HelpLink: https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/CSLADP010-InvalidAsyncSuffix.md, MessageFormat: The CslaDataPortalExtensionsAsyncSuffix value '{0}' is not valid in a C# identifier; generated async data portal extension methods use the 'Async' suffix, Category: Csla.DataPortal, DefaultSeverity: Warning, diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidPrefix.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidPrefix.verified.txt index 5d7e106812..7ce83d14a8 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidPrefix.verified.txt +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidPrefix.verified.txt @@ -15,6 +15,7 @@ Descriptor: { Id: CSLADP008, Title: Invalid DataPortalExtensions prefix, + HelpLink: https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/CSLADP008-InvalidExtensionPrefix.md, MessageFormat: Data portal extension methods are not generated for '{0}' because the prefix '{1}' is not a valid C# identifier, Category: Csla.DataPortal, DefaultSeverity: Warning, diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidTargets.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidTargets.verified.txt index aa238de717..2cc54ba51d 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidTargets.verified.txt +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.InvalidTargets.verified.txt @@ -15,6 +15,7 @@ Descriptor: { Id: CSLADP004, Title: Invalid target for DataPortalExtensions, + HelpLink: https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/CSLADP004-InvalidExtensionsTarget.md, MessageFormat: Data portal extension methods are not generated for '{0}': {1}, Category: Csla.DataPortal, DefaultSeverity: Warning, @@ -35,6 +36,7 @@ Descriptor: { Id: CSLADP004, Title: Invalid target for DataPortalExtensions, + HelpLink: https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/CSLADP004-InvalidExtensionsTarget.md, MessageFormat: Data portal extension methods are not generated for '{0}': {1}, Category: Csla.DataPortal, DefaultSeverity: Warning, diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.NoAsyncSuffixWithSync.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.NoAsyncSuffixWithSync.verified.txt index a5d4171662..59ac0c1714 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.NoAsyncSuffixWithSync.verified.txt +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.NoAsyncSuffixWithSync.verified.txt @@ -8,6 +8,7 @@ Descriptor: { Id: CSLADP011, Title: Synchronous data portal extensions are not generated without an async suffix, + HelpLink: https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/CSLADP011-SyncExtensionsNotGenerated.md, MessageFormat: Synchronous data portal extension methods are not generated because CslaDataPortalExtensionsAsyncSuffix is 'none', so they would have the same names as the async methods; set CslaGenerateSyncDataPortalExtensions to false, Category: Csla.DataPortal, DefaultSeverity: Warning, diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ParameterAccessibility.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ParameterAccessibility.verified.txt index 7e1e6182e4..ed8a0a4d4e 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ParameterAccessibility.verified.txt +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalExtensions/Snapshots/DataPortalExtensionsGeneratorTests.ParameterAccessibility.verified.txt @@ -15,6 +15,7 @@ Descriptor: { Id: CSLADP006, Title: Operation parameter type is not accessible, + HelpLink: https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/CSLADP006-InaccessibleParameterType.md, MessageFormat: The data portal extension method for '{0}' cannot be generated because parameter '{1}' has type '{2}', which is not accessible outside its containing type, Category: Csla.DataPortal, DefaultSeverity: Warning, diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsGeneratorTests.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsGeneratorTests.cs index 747fac11b5..fded39fbe1 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsGeneratorTests.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsGeneratorTests.cs @@ -656,6 +656,28 @@ private void Fetch(int id) { } await TestHelperVerify(source); } + [TestMethod("Business objects without operation methods generate nothing")] + public async Task NoOperationMethods() + { + var source = """ + using Csla; + + namespace TestApp + { + public partial class PersonEdit : Csla.BusinessBase + { + } + + public partial class PersonList : Csla.ReadOnlyListBase + { + private void Fetch(int id) { } + } + } + """; + + await TestHelperVerify(source); + } + [TestMethod("Class that implements the named mapping itself only gets the operations interface and type mapping")] public async Task ExistingNamedMapping() { diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsTestHelper.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsTestHelper.cs index 4df31962c0..f250d0ba2b 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsTestHelper.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/DataPortalOperationsTestHelper.cs @@ -104,7 +104,9 @@ public static (GeneratorDriver Driver, CSharpCompilation Compilation) Setup(stri .Where(a => !a.IsDynamic && !string.IsNullOrWhiteSpace(a.Location)) .Select(a => MetadataReference.CreateFromFile(a.Location)) .Concat([ - MetadataReference.CreateFromFile(typeof(FetchAttribute).Assembly.Location) + MetadataReference.CreateFromFile(typeof(FetchAttribute).Assembly.Location), + // Generated dispatch resolves [Inject] parameters with these extension methods. + MetadataReference.CreateFromFile(typeof(Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions).Assembly.Location) ]); var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOperationName.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOperationName.verified.txt index 5f4412a93b..894216f186 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOperationName.verified.txt +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.AmbiguousOperationName.verified.txt @@ -15,6 +15,7 @@ Descriptor: { Id: CSLADP009, Title: Data portal operation methods are ambiguous, + HelpLink: https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/CSLADP009-AmbiguousOperationName.md, MessageFormat: Methods '{0}' and '{1}' on '{2}' both map to data portal operation '{3}' with the same number of injected parameters; the data portal cannot choose between them and reports an ambiguous match, Category: Csla.DataPortal, DefaultSeverity: Warning, diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs index f724a4e024..278a334932 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.ChildOperations#TestApp.LineItem.DataPortalOperations.g.verified.cs @@ -68,7 +68,7 @@ internal interface IDataPortalOperations global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "InsertChild"); try { - var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c2i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(serviceProvider, typeof(global::TestApp.IDal)))!; await __operations.InsertChild(__c2i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -85,7 +85,7 @@ internal interface IDataPortalOperations global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "UpdateChild"); try { - var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c3i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(serviceProvider, typeof(global::TestApp.IDal)))!; await __operations.UpdateChild(__c3i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -102,7 +102,7 @@ internal interface IDataPortalOperations global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "DeleteSelfChild"); try { - var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c4i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(serviceProvider, typeof(global::TestApp.IDal)))!; await __operations.DeleteSelfChild(__c4i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -155,7 +155,7 @@ internal interface IDataPortalOperations global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "InsertChild"); try { - var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c2i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(serviceProvider, typeof(global::TestApp.IDal)))!; await __operations.InsertChild(__c2i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -171,7 +171,7 @@ internal interface IDataPortalOperations global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "UpdateChild"); try { - var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c3i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(serviceProvider, typeof(global::TestApp.IDal)))!; await __operations.UpdateChild(__c3i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -187,7 +187,7 @@ internal interface IDataPortalOperations global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "DeleteSelfChild"); try { - var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c4i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(serviceProvider, typeof(global::TestApp.IDal)))!; await __operations.DeleteSelfChild(__c4i0).ConfigureAwait(false); } catch (global::System.Exception __ex) diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 37239bf8b7..f5673b76b8 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.CompleteCrud#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -68,7 +68,7 @@ internal interface IDataPortalOperations global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Insert"); try { - var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c2i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(serviceProvider, typeof(global::TestApp.IDal)))!; await __operations.Insert(__c2i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -85,7 +85,7 @@ internal interface IDataPortalOperations global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Update"); try { - var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c3i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(serviceProvider, typeof(global::TestApp.IDal)))!; await __operations.Update(__c3i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -102,7 +102,7 @@ internal interface IDataPortalOperations global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "DeleteSelf"); try { - var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c4i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(serviceProvider, typeof(global::TestApp.IDal)))!; await __operations.DeleteSelf(__c4i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -155,7 +155,7 @@ internal interface IDataPortalOperations global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Insert"); try { - var __c2i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c2i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(serviceProvider, typeof(global::TestApp.IDal)))!; await __operations.Insert(__c2i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -171,7 +171,7 @@ internal interface IDataPortalOperations global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Update"); try { - var __c3i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c3i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(serviceProvider, typeof(global::TestApp.IDal)))!; await __operations.Update(__c3i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -187,7 +187,7 @@ internal interface IDataPortalOperations global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "DeleteSelf"); try { - var __c4i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c4i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(serviceProvider, typeof(global::TestApp.IDal)))!; await __operations.DeleteSelf(__c4i0).ConfigureAwait(false); } catch (global::System.Exception __ex) diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 8e23c1ed83..ba756baa31 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -33,7 +33,7 @@ internal interface IDataPortalOperations { try { - var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c0i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(serviceProvider, typeof(global::TestApp.IDal)))!; __operations.Fetch__Int32(__c0p0, __c0i0); } catch (global::System.Exception __ex) @@ -57,7 +57,7 @@ internal interface IDataPortalOperations { try { - var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c0i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(serviceProvider, typeof(global::TestApp.IDal)))!; __operations.Fetch__Int32(__c0p0, __c0i0); } catch (global::System.Exception __ex) diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName.verified.txt b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName.verified.txt index ba002a4413..28768b3587 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName.verified.txt +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.DuplicateOperationName.verified.txt @@ -15,6 +15,7 @@ Descriptor: { Id: CSLADP002, Title: Data portal operation methods share an operation name, + HelpLink: https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/CSLADP002-DuplicateOperationName.md, MessageFormat: Methods '{0}' and '{1}' on '{2}' both map to data portal operation '{3}'; name-based dispatch uses '{0}', Category: Csla.DataPortal, DefaultSeverity: Info, @@ -35,6 +36,7 @@ Descriptor: { Id: CSLADP002, Title: Data portal operation methods share an operation name, + HelpLink: https://github.com/MarimerLLC/csla/tree/main/docs/analyzers/CSLADP002-DuplicateOperationName.md, MessageFormat: Methods '{0}' and '{1}' on '{2}' both map to data portal operation '{3}'; name-based dispatch uses '{0}', Category: Csla.DataPortal, DefaultSeverity: Info, diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InCriteriaAndNullableInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InCriteriaAndNullableInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 36f4686d43..febe36be00 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InCriteriaAndNullableInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InCriteriaAndNullableInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -31,8 +31,8 @@ internal interface IDataPortalOperations { try { - var __c0i0 = (int?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(int?), true)); - var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), true)); + var __c0i0 = (int?)(serviceProvider.GetService(typeof(int?))); + var __c0i1 = (global::TestApp.IDal?)(serviceProvider.GetService(typeof(global::TestApp.IDal))); __operations.Create(__c0i0, __c0i1); } catch (global::System.Exception __ex) @@ -86,8 +86,8 @@ internal interface IDataPortalOperations { try { - var __c1i0 = (int?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(int?), true)); - var __c1i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), true)); + var __c1i0 = (int?)(serviceProvider.GetService(typeof(int?))); + var __c1i1 = (global::TestApp.IDal?)(serviceProvider.GetService(typeof(global::TestApp.IDal))); __operations.Create(__c1i0, __c1i1); } catch (global::System.Exception __ex) diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InjectBeforeCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InjectBeforeCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 1a53d1a8ae..85c75718ea 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InjectBeforeCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.InjectBeforeCriteria#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -29,8 +29,8 @@ internal interface IDataPortalOperations { try { - var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), true)); + var __c0i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(serviceProvider, typeof(global::TestApp.IDal)))!; + var __c0i1 = (global::TestApp.IDal?)(serviceProvider.GetService(typeof(global::TestApp.IDal))); __operations.Fetch__Int32_String(__c0i0, __c0p0, __c0i1, __c0p1); } catch (global::System.Exception __ex) @@ -54,8 +54,8 @@ internal interface IDataPortalOperations { try { - var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; - var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), true)); + var __c0i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(serviceProvider, typeof(global::TestApp.IDal)))!; + var __c0i1 = (global::TestApp.IDal?)(serviceProvider.GetService(typeof(global::TestApp.IDal))); __operations.Fetch__Int32_String(__c0i0, __c0p0, __c0i1, (string)criteria[1]!); } catch (global::System.Exception __ex) diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.KeyedInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.KeyedInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 4e8d387876..83860fee03 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.KeyedInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.KeyedInject#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -31,8 +31,8 @@ internal interface IDataPortalOperations { try { - var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), (short)(-1), false))!; - var __c0i1 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), (byte)2, false))!; + var __c0i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderKeyedServiceExtensions.GetRequiredKeyedService(serviceProvider, typeof(global::TestApp.IDal), (short)(-1)))!; + var __c0i1 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderKeyedServiceExtensions.GetRequiredKeyedService(serviceProvider, typeof(global::TestApp.IDal), (byte)2))!; __operations.Create(__c0i0, __c0i1); } catch (global::System.Exception __ex) @@ -48,8 +48,8 @@ internal interface IDataPortalOperations { try { - var __c1i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), "primary", false))!; - var __c1i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), global::TestApp.DalKind.Secondary, true)); + var __c1i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderKeyedServiceExtensions.GetRequiredKeyedService(serviceProvider, typeof(global::TestApp.IDal), "primary"))!; + var __c1i1 = (global::TestApp.IDal?)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderKeyedServiceExtensions.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), global::TestApp.DalKind.Secondary)); __operations.Fetch__Int32(__c1p0, __c1i0, __c1i1); } catch (global::System.Exception __ex) @@ -73,8 +73,8 @@ internal interface IDataPortalOperations { try { - var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), "primary", false))!; - var __c0i1 = (global::TestApp.IDal?)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), global::TestApp.DalKind.Secondary, true)); + var __c0i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderKeyedServiceExtensions.GetRequiredKeyedService(serviceProvider, typeof(global::TestApp.IDal), "primary"))!; + var __c0i1 = (global::TestApp.IDal?)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderKeyedServiceExtensions.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), global::TestApp.DalKind.Secondary)); __operations.Fetch__Int32(__c0p0, __c0i0, __c0i1); } catch (global::System.Exception __ex) @@ -89,8 +89,8 @@ internal interface IDataPortalOperations { try { - var __c1i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), (short)(-1), false))!; - var __c1i1 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetKeyedService(serviceProvider, typeof(global::TestApp.IDal), (byte)2, false))!; + var __c1i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderKeyedServiceExtensions.GetRequiredKeyedService(serviceProvider, typeof(global::TestApp.IDal), (short)(-1)))!; + var __c1i1 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderKeyedServiceExtensions.GetRequiredKeyedService(serviceProvider, typeof(global::TestApp.IDal), (byte)2))!; __operations.Create(__c1i0, __c1i1); } catch (global::System.Exception __ex) diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index aff3acd97a..3d662c9cd2 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectAllowNull#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -30,7 +30,7 @@ internal interface IDataPortalOperations global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); try { - var __c0i0 = (global::TestApp.ILogger?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.ILogger), true)); + var __c0i0 = (global::TestApp.ILogger?)(serviceProvider.GetService(typeof(global::TestApp.ILogger))); await __operations.Fetch__Int32(__c0p0, __c0i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -55,7 +55,7 @@ internal interface IDataPortalOperations global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Fetch"); try { - var __c0i0 = (global::TestApp.ILogger?)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.ILogger), true)); + var __c0i0 = (global::TestApp.ILogger?)(serviceProvider.GetService(typeof(global::TestApp.ILogger))); await __operations.Fetch__Int32(__c0p0, __c0i0).ConfigureAwait(false); } catch (global::System.Exception __ex) diff --git a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs index 64a47cc28c..9f7d9f1205 100644 --- a/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs +++ b/Source/tests/Csla.Generator.AutoImplementProperties.CSharp.Tests/DataPortalOperations/Snapshots/DataPortalOperationsGeneratorTests.OperationWithInjectParam#TestApp.PersonEdit.DataPortalOperations.g.verified.cs @@ -30,7 +30,7 @@ internal interface IDataPortalOperations global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Insert"); try { - var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c0i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(serviceProvider, typeof(global::TestApp.IDal)))!; await __operations.Insert(__c0i0).ConfigureAwait(false); } catch (global::System.Exception __ex) @@ -55,7 +55,7 @@ internal interface IDataPortalOperations global::Csla.Server.DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(this, isSync, serviceProvider, "Insert"); try { - var __c0i0 = (global::TestApp.IDal)(global::Csla.Server.DataPortalOperationHelper.GetService(serviceProvider, typeof(global::TestApp.IDal), false))!; + var __c0i0 = (global::TestApp.IDal)(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(serviceProvider, typeof(global::TestApp.IDal)))!; await __operations.Insert(__c0i0).ConfigureAwait(false); } catch (global::System.Exception __ex) diff --git a/Source/tests/csla.netcore.test/DataPortal/DataPortalExtensionsTests.cs b/Source/tests/csla.netcore.test/DataPortal/DataPortalExtensionsTests.cs index a3cc85aea7..c3ac0e16cd 100644 --- a/Source/tests/csla.netcore.test/DataPortal/DataPortalExtensionsTests.cs +++ b/Source/tests/csla.netcore.test/DataPortal/DataPortalExtensionsTests.cs @@ -373,10 +373,6 @@ public void OperationHelper_NullArguments_Throw() .Should().Throw().WithParameterName("target"); FluentActions.Invoking(() => DataPortalOperationHelper.ThrowIfAsyncMethodOnSyncClient(target, true, null!, "Fetch")) .Should().Throw().WithParameterName("serviceProvider"); - FluentActions.Invoking(() => DataPortalOperationHelper.GetService(services, null!, true)) - .Should().Throw().WithParameterName("serviceType"); - FluentActions.Invoking(() => DataPortalOperationHelper.GetKeyedService(services, null!, "key", true)) - .Should().Throw().WithParameterName("serviceType"); } #endregion diff --git a/docs/analyzers/CSLADP002-DuplicateOperationName.md b/docs/analyzers/CSLADP002-DuplicateOperationName.md new file mode 100644 index 0000000000..b47de2b25a --- /dev/null +++ b/docs/analyzers/CSLADP002-DuplicateOperationName.md @@ -0,0 +1,37 @@ +# Data portal operation methods share an operation name + +## Issue + +This diagnostic is reported (as information) when two data portal operation methods on a `partial` class map to the same data portal operation. That happens when the methods have the same operation attribute and the same criteria parameter types, and differ only in their `[Inject]` parameters. For example: + +``` +using Csla; +using System; + +[Serializable] +public partial class Customer + : BusinessBase +{ + [Fetch] + private void Fetch(int id) { } + + [Fetch] + private void Fetch(int id, [Inject] ICustomerDal dal) { } +} +``` + +A call such as `portal.FetchAsync(42)` matches both methods. The data portal picks the method with more injected parameters, which is `Fetch(int, ICustomerDal)` here. This is the same choice that reflection-based dispatch makes, and the code generated for the class makes it too. The diagnostic message names the method that is used. + +The data portal extension methods generator also generates extension methods only for the method that is used, because both methods would produce the same extension method. + +## How to fix + +If the selected method is the one you intend to be called, no change is needed. Otherwise, remove the method that is not used, or change the criteria parameters so that each method maps to a different operation. + +If two methods have the same number of injected parameters, the data portal cannot choose between them, and [CSLADP009](CSLADP009-AmbiguousOperationName.md) is reported instead. + +You can suppress this diagnostic in source or in an `.editorconfig` file: + +``` +dotnet_diagnostic.CSLADP002.severity = none +``` diff --git a/docs/analyzers/CSLADP003-GenericExtensionsNotGenerated.md b/docs/analyzers/CSLADP003-GenericExtensionsNotGenerated.md new file mode 100644 index 0000000000..e3d6a44575 --- /dev/null +++ b/docs/analyzers/CSLADP003-GenericExtensionsNotGenerated.md @@ -0,0 +1,33 @@ +# Data portal extensions are not generated for generic types + +## Issue + +This diagnostic is reported (as information) when a class marked with `[DataPortalExtensions]` is generic, or is nested in a generic type. For example: + +``` +using Csla; +using System; + +[Serializable] +[DataPortalExtensions] +public partial class LookupList + : ReadOnlyListBase, T> +{ + [Fetch] + private void Fetch(int id) { } +} +``` + +Extension methods on `IDataPortal>` would need their own type parameters and constraints, so the data portal extension methods generator does not generate them for generic types. + +The data portal still calls the operation methods of a generic `partial` class directly. Only the extension methods are not generated. + +## How to fix + +Call the `IDataPortal` methods directly, for example `portal.FetchAsync(42)`, or remove `[DataPortalExtensions]` from the class. + +You can suppress this diagnostic in source or in an `.editorconfig` file: + +``` +dotnet_diagnostic.CSLADP003.severity = none +``` diff --git a/docs/analyzers/CSLADP004-InvalidExtensionsTarget.md b/docs/analyzers/CSLADP004-InvalidExtensionsTarget.md new file mode 100644 index 0000000000..8aece967a3 --- /dev/null +++ b/docs/analyzers/CSLADP004-InvalidExtensionsTarget.md @@ -0,0 +1,33 @@ +# Invalid target for DataPortalExtensions + +## Issue + +This diagnostic is reported when `[DataPortalExtensions]` is applied to a class that cannot have data portal extension methods. The message gives the reason, which is one of: + +* The class does not implement `Csla.Core.ICslaObject`, so it is not a CSLA business type. +* The class is abstract, so the data portal cannot create it. +* The class is not accessible outside its containing type (for example, a `private` nested class), so extension methods in the containing namespace cannot refer to it. + +For example: + +``` +using Csla; +using System; + +[Serializable] +[DataPortalExtensions] +public abstract partial class CustomerBase + : BusinessBase +{ + [Fetch] + private void Fetch(int id) { } +} +``` + +No extension methods are generated for the class. + +When the assembly is marked with `[assembly: DataPortalExtensions]`, classes that cannot have extension methods are skipped without a diagnostic. + +## How to fix + +Remove `[DataPortalExtensions]` from the class, or apply it to the concrete business class instead. diff --git a/docs/analyzers/CSLADP005-ExtensionNameHidden.md b/docs/analyzers/CSLADP005-ExtensionNameHidden.md new file mode 100644 index 0000000000..e61e0a6888 --- /dev/null +++ b/docs/analyzers/CSLADP005-ExtensionNameHidden.md @@ -0,0 +1,35 @@ +# Generated data portal extension would be hidden + +## Issue + +This diagnostic is reported when the name of a generated data portal extension method is the same as the name of an instance method on `IDataPortal` or `IChildDataPortal`. For example: + +``` +using Csla; +using System; + +[Serializable] +[DataPortalExtensions] +public partial class Customer + : BusinessBase +{ + [Fetch] + private void Fetch(int id) { } +} +``` + +The generated method names come from the operation method name, so this class would get `FetchAsync(int id)` and `Fetch(int id)` extension methods. `IDataPortal` already has `FetchAsync(params object?[]? criteria)` and `Fetch(params object?[]? criteria)` instance methods. C# always prefers an applicable instance method over an extension method, so a call such as `portal.FetchAsync(42)` would call the untyped instance method, and the extension method could never be called that way. + +The extension method is not generated. + +## How to fix + +Set `Prefix` on the attribute to give the generated methods distinct names: + +``` +[DataPortalExtensions(Prefix = "Portal")] +public partial class Customer + : BusinessBase +``` + +The class now gets `PortalFetchAsync(int id)` and `PortalFetch(int id)`. Alternatively, give the operation method a more specific name, such as `FetchById`, which produces `FetchByIdAsync(int id)`. diff --git a/docs/analyzers/CSLADP006-InaccessibleParameterType.md b/docs/analyzers/CSLADP006-InaccessibleParameterType.md new file mode 100644 index 0000000000..2232e3d212 --- /dev/null +++ b/docs/analyzers/CSLADP006-InaccessibleParameterType.md @@ -0,0 +1,31 @@ +# Operation parameter type is not accessible + +## Issue + +This diagnostic is reported when a criteria parameter of a data portal operation method has a type that is not accessible outside its containing type, such as a `private` nested class. For example: + +``` +using Csla; +using System; + +[Serializable] +[DataPortalExtensions] +public partial class Customer + : BusinessBase +{ + [Serializable] + private class Criteria + { + public int Id { get; set; } + } + + [Fetch] + private void Fetch(Criteria criteria) { } +} +``` + +Data portal extension methods are generated in a separate static class, so their parameters cannot use a type that only `Customer` can see. The extension methods for this operation method are not generated. Extension methods for the other operation methods of the class are still generated. + +## How to fix + +Make the criteria type `internal` or `public`, or pass the criteria values as separate parameters. diff --git a/docs/analyzers/CSLADP007-DuplicateExtensionMethod.md b/docs/analyzers/CSLADP007-DuplicateExtensionMethod.md new file mode 100644 index 0000000000..8bdf0ea15e --- /dev/null +++ b/docs/analyzers/CSLADP007-DuplicateExtensionMethod.md @@ -0,0 +1,28 @@ +# Generated data portal extension would be duplicated + +## Issue + +This diagnostic is reported when two operation methods of a class would produce data portal extension methods with the same name and parameter types. For example: + +``` +using Csla; +using System; + +[Serializable] +[DataPortalExtensions] +public partial class Customer + : BusinessBase +{ + [Create] + private void Load(int id) { } + + [Fetch] + private void Load(int id, [Inject] ICustomerDal dal) { } +} +``` + +Both methods would produce `LoadAsync(int id)` and `Load(int id)` extension methods on `IDataPortal`, because injected parameters are not part of the extension method. The extension methods are generated for only one of the methods; the diagnostic names the method that does not get them. + +## How to fix + +Give the operation methods distinct names, such as `Create` and `Fetch`, so that each produces its own extension methods. Alternatively, mark the method that does not need an extension method with `[NoDataPortalExtension]`. diff --git a/docs/analyzers/CSLADP008-InvalidExtensionPrefix.md b/docs/analyzers/CSLADP008-InvalidExtensionPrefix.md new file mode 100644 index 0000000000..7957bc87a7 --- /dev/null +++ b/docs/analyzers/CSLADP008-InvalidExtensionPrefix.md @@ -0,0 +1,25 @@ +# Invalid DataPortalExtensions prefix + +## Issue + +This diagnostic is reported when the `Prefix` of a `[DataPortalExtensions]` attribute would not produce valid C# method names. For example: + +``` +using Csla; +using System; + +[Serializable] +[DataPortalExtensions(Prefix = "My-")] +public partial class Customer + : BusinessBase +{ + [Fetch] + private void Fetch(int id) { } +} +``` + +No extension methods are generated for the class. When the prefix is set on an assembly attribute, `[assembly: DataPortalExtensions(Prefix = "...")]`, the diagnostic is reported once, on the assembly attribute, and no extension methods are generated for classes that use the assembly prefix. + +## How to fix + +Use a prefix that is a valid C# identifier, such as `Portal` or `My`. diff --git a/docs/analyzers/CSLADP009-AmbiguousOperationName.md b/docs/analyzers/CSLADP009-AmbiguousOperationName.md new file mode 100644 index 0000000000..d11c915453 --- /dev/null +++ b/docs/analyzers/CSLADP009-AmbiguousOperationName.md @@ -0,0 +1,31 @@ +# Data portal operation methods are ambiguous + +## Issue + +This diagnostic is reported when two data portal operation methods on a `partial` class map to the same data portal operation and have the same number of `[Inject]` parameters. For example: + +``` +using Csla; +using System; + +[Serializable] +public partial class Customer + : BusinessBase +{ + [Fetch] + private void Fetch(int id, [Inject] ICustomerDal dal) { } + + [Fetch] + private void Fetch(int id, [Inject] IAuditLog log) { } +} +``` + +A call such as `portal.FetchAsync(42)` matches both methods, and the data portal has no way to choose between them. At run time the data portal throws an `AmbiguousMatchException`. + +The code generated for the class does not dispatch these methods directly, so the data portal falls back to reflection-based dispatch and throws the same exception. Calling the operation through a generated data portal extension method fails the same way. + +## How to fix + +Remove one of the methods, or change the criteria parameters so that each method maps to a different operation. If the methods need different services, combine them into one method that injects every service it needs. + +When the methods have different numbers of injected parameters, the data portal uses the method with more injected parameters, and [CSLADP002](CSLADP002-DuplicateOperationName.md) is reported instead. diff --git a/docs/analyzers/CSLADP010-InvalidAsyncSuffix.md b/docs/analyzers/CSLADP010-InvalidAsyncSuffix.md new file mode 100644 index 0000000000..4679d9755a --- /dev/null +++ b/docs/analyzers/CSLADP010-InvalidAsyncSuffix.md @@ -0,0 +1,25 @@ +# Invalid data portal extensions async suffix + +## Issue + +This diagnostic is reported when the `CslaDataPortalExtensionsAsyncSuffix` MSBuild property is set to a value that cannot be used in a C# method name. For example: + +```xml + + -Async + +``` + +The value is ignored, and the generated async data portal extension methods use the default `Async` suffix. The diagnostic is reported only when the project generates data portal extension methods. + +## How to fix + +Set the property to a valid identifier, such as `Async` or `Task`, or to `none` for no suffix: + +```xml + + none + +``` + +With no suffix, the synchronous extension methods are not generated; see [CSLADP011](CSLADP011-SyncExtensionsNotGenerated.md). diff --git a/docs/analyzers/CSLADP011-SyncExtensionsNotGenerated.md b/docs/analyzers/CSLADP011-SyncExtensionsNotGenerated.md new file mode 100644 index 0000000000..d77255b93b --- /dev/null +++ b/docs/analyzers/CSLADP011-SyncExtensionsNotGenerated.md @@ -0,0 +1,26 @@ +# Synchronous data portal extensions are not generated without an async suffix + +## Issue + +This diagnostic is reported when `CslaDataPortalExtensionsAsyncSuffix` is `none` and `CslaGenerateSyncDataPortalExtensions` is not set to `false`: + +```xml + + none + +``` + +Without an async suffix, the async method for a `Fetch` operation method is named `Fetch`. The synchronous method would have the same name and the same parameters, so it cannot be generated. Only the async extension methods are generated. The diagnostic is reported only when the project generates data portal extension methods. + +## How to fix + +If you only want async extension methods, which is the usual reason for removing the suffix, turn off the synchronous methods: + +```xml + + none + false + +``` + +If you need the synchronous methods, use an async suffix, such as the default `Async`. diff --git a/docs/analyzers/index.md b/docs/analyzers/index.md index 66fe06c653..fd95d0e858 100644 --- a/docs/analyzers/index.md +++ b/docs/analyzers/index.md @@ -47,3 +47,17 @@ If you have an idea for an analyzer that would be beneficial for developers usin * [CSLA0022](CSLA0022-ObjectAuthorizationRulesStatic.md) - Object authorization rules configuration should be declared as static * [CSLA0024](CSLA0024-TypeWithOperationsShouldBePartialAnalyzer.md) - Types with data portal operation methods should be partial * [CSLA0025](CSLA0025-UseGeneratedDataPortalExtensionAnalyzer.md) - Use the generated data portal extension method + +## Data Portal Generator Diagnostics +These diagnostics come from the data portal operations and data portal extensions source generators included in the `Csla` package. They report operation methods that the generated code handles differently from what you might expect, and data portal extension methods that are not generated. They are reported by analyzers, so you can suppress them in source or in an `.editorconfig` file. + +* [CSLADP002](CSLADP002-DuplicateOperationName.md) - Data portal operation methods share an operation name +* [CSLADP003](CSLADP003-GenericExtensionsNotGenerated.md) - Data portal extensions are not generated for generic types +* [CSLADP004](CSLADP004-InvalidExtensionsTarget.md) - Invalid target for `[DataPortalExtensions]` +* [CSLADP005](CSLADP005-ExtensionNameHidden.md) - Generated data portal extension would be hidden +* [CSLADP006](CSLADP006-InaccessibleParameterType.md) - Operation parameter type is not accessible +* [CSLADP007](CSLADP007-DuplicateExtensionMethod.md) - Generated data portal extension would be duplicated +* [CSLADP008](CSLADP008-InvalidExtensionPrefix.md) - Invalid `[DataPortalExtensions]` prefix +* [CSLADP009](CSLADP009-AmbiguousOperationName.md) - Data portal operation methods are ambiguous +* [CSLADP010](CSLADP010-InvalidAsyncSuffix.md) - Invalid data portal extensions async suffix +* [CSLADP011](CSLADP011-SyncExtensionsNotGenerated.md) - Synchronous data portal extensions are not generated without an async suffix diff --git a/docs/code-generators.md b/docs/code-generators.md index 34f31ef796..11a8cf20d3 100644 --- a/docs/code-generators.md +++ b/docs/code-generators.md @@ -5,8 +5,8 @@ Starting with CSLA 9, there are code generators available as NuGet packages. These packages are designed to work with the CSLA .NET framework to generate code for your business objects as part of the .NET build process. * [AutoImplementProperties](https://github.com/MarimerLLC/csla/tree/main/Source/Csla.Generators/cs/AutoImplementProperties) - Uses code generation to radically reduce the amount of code you need to write to declare properties in a business domain class. Starting with CSLA 11 this generator is included in the `Csla` package. -* Data portal operations - Starting with CSLA 11, included in the `Csla` package. For every `partial` class with data portal operation methods, generates code that lets the data portal call the operation methods directly instead of through reflection. -* Data portal extensions - Starting with CSLA 11, included in the `Csla` package. For classes marked with `[DataPortalExtensions]` (or every eligible class, when the assembly is marked), generates strongly typed extension methods on `IDataPortal` and `IChildDataPortal` for each operation method, so calls such as `portal.GetByIdAsync(42)` are checked by the compiler. Adapted from Stefan Ossendorf's [Csla.DataPortalExtensions](https://github.com/StefanOssendorf/Csla.DataPortalExtensions). +* [DataPortalOperations](data-portal-operations-generator.md) - Starting with CSLA 11, included in the `Csla` package. For every `partial` class with data portal operation methods, generates code that lets the data portal call the operation methods directly instead of through reflection. +* [DataPortalExtensions](data-portal-extensions-generator.md) - Starting with CSLA 11, included in the `Csla` package. For classes marked with `[DataPortalExtensions]` (or every eligible class, when the assembly is marked), generates strongly typed extension methods on `IDataPortal` and `IChildDataPortal` for each operation method, so calls such as `portal.GetByIdAsync(42)` are checked by the compiler. Adapted from Stefan Ossendorf's [Csla.DataPortalExtensions](https://github.com/StefanOssendorf/Csla.DataPortalExtensions). * [AutoSerialization](https://github.com/MarimerLLC/csla/tree/main/Source/Csla.Generators/cs/AutoSerialization) - Uses code generation to improve serialization performance Other modern code generator tools also exist: diff --git a/docs/data-portal-extensions-generator.md b/docs/data-portal-extensions-generator.md new file mode 100644 index 0000000000..e3b35ac8b4 --- /dev/null +++ b/docs/data-portal-extensions-generator.md @@ -0,0 +1,101 @@ +# Data Portal Extensions Generator + +Starting with CSLA 11, the `Csla` package includes a source generator that creates strongly typed extension methods on `IDataPortal` and `IChildDataPortal` for the data portal operation methods of your business classes. Instead of passing untyped criteria: + +```csharp +var person = await portal.FetchAsync(42); +``` + +you call a method whose parameters match the operation method, so the compiler checks the criteria: + +```csharp +var person = await portal.PortalFetchAsync(42); +``` + +The generator and the related [CSLA0025](analyzers/CSLA0025-UseGeneratedDataPortalExtensionAnalyzer.md) analyzer are adapted from [Csla.DataPortalExtensions](https://github.com/StefanOssendorf/Csla.DataPortalExtensions) by [Stefan Ossendorf](https://github.com/StefanOssendorf), used under the MIT License. For CSLA 11 and later, use the generator included in the `Csla` package instead of the `Csla.DataPortalExtensions` package. + +## Using the generator + +Add `[DataPortalExtensions]` to a business class: + +```csharp +[DataPortalExtensions(Prefix = "Portal")] +public partial class PersonEdit : BusinessBase +{ + [Create] + private void Create() { } + + [Fetch] + private async Task Fetch(int id, [Inject] IPersonDal dal) { /* ... */ } +} +``` + +The generator creates a `PersonEditDataPortalExtensions` class, in the namespace of `PersonEdit`, with these methods: + +```csharp +Task PortalCreateAsync(this IDataPortal portal); +PersonEdit PortalCreate(this IDataPortal portal); +Task PortalFetchAsync(this IDataPortal portal, int id); +PersonEdit PortalFetch(this IDataPortal portal, int id); +``` + +Extension methods are generated for `[Create]`, `[Fetch]`, `[Execute]` and `[Delete]` methods on `IDataPortal`, and for `[CreateChild]` and `[FetchChild]` methods on `IChildDataPortal`. + +* The method names come from the operation method name, without an `Async` suffix, plus the `Prefix`. +* The methods take only the criteria parameters. `[Inject]` parameters are resolved on the server as usual. Parameter default values are kept. +* The methods pass the operation name to the data portal, so the data portal calls the operation method directly instead of searching for it using reflection. With a custom or mock `IDataPortal` implementation, the methods call the existing `params object[]` methods instead. + +### Choosing a prefix + +`IDataPortal` already has instance methods such as `FetchAsync` and `Fetch`, and C# always prefers an instance method over an extension method. An operation method named `Fetch` would therefore produce extension methods that are hidden by the instance methods. These are not generated, and analyzer [CSLADP005](analyzers/CSLADP005-ExtensionNameHidden.md) reports them. + +Use `Prefix` to give the generated methods distinct names, or give the operation methods more specific names, such as `FetchById`. + +### Enabling extensions for an assembly + +Add the attribute to the assembly to generate extension methods for every business class in the assembly that can have them: + +```csharp +[assembly: DataPortalExtensions(Prefix = "Portal")] +``` + +This applies to non-abstract, non-generic classes that implement `ICslaObject` and are accessible outside their containing type. A `Prefix` on a class attribute takes precedence over the assembly `Prefix`. + +### Excluding classes and methods + +Add `[NoDataPortalExtension]` to a class or an operation method to exclude it. + +## Build properties + +MSBuild properties in the project file control the generated methods: + +```xml + + + false + + none + +``` + +With no async suffix, the sync methods would have the same names as the async methods, so they are not generated; see [CSLADP011](analyzers/CSLADP011-SyncExtensionsNotGenerated.md). + +## Diagnostics + +The analyzers in the `Csla` package report extension methods that are not generated, and why: + +* [CSLADP003](analyzers/CSLADP003-GenericExtensionsNotGenerated.md) - Data portal extensions are not generated for generic types +* [CSLADP004](analyzers/CSLADP004-InvalidExtensionsTarget.md) - Invalid target for `[DataPortalExtensions]` +* [CSLADP005](analyzers/CSLADP005-ExtensionNameHidden.md) - Generated data portal extension would be hidden +* [CSLADP006](analyzers/CSLADP006-InaccessibleParameterType.md) - Operation parameter type is not accessible +* [CSLADP007](analyzers/CSLADP007-DuplicateExtensionMethod.md) - Generated data portal extension would be duplicated +* [CSLADP008](analyzers/CSLADP008-InvalidExtensionPrefix.md) - Invalid `[DataPortalExtensions]` prefix +* [CSLADP010](analyzers/CSLADP010-InvalidAsyncSuffix.md) - Invalid data portal extensions async suffix +* [CSLADP011](analyzers/CSLADP011-SyncExtensionsNotGenerated.md) - Synchronous data portal extensions are not generated without an async suffix + +Analyzer [CSLA0025](analyzers/CSLA0025-UseGeneratedDataPortalExtensionAnalyzer.md) suggests using a generated extension method instead of calling an `IDataPortal` method with untyped criteria, and provides a code fix that replaces the call. + +## Related + +* [Data portal operations generator](data-portal-operations-generator.md) +* [Analyzers](analyzers/index.md) diff --git a/docs/data-portal-operations-generator.md b/docs/data-portal-operations-generator.md new file mode 100644 index 0000000000..ff53f22d0e --- /dev/null +++ b/docs/data-portal-operations-generator.md @@ -0,0 +1,57 @@ +# Data Portal Operations Generator + +Starting with CSLA 11, the `Csla` package includes a source generator that lets the data portal call your data portal operation methods directly, instead of finding and invoking them through reflection. There is nothing to install or configure: the generator runs in every C# project that references `Csla`. + +## Using the generator + +Declare your business class `partial`. Every class that declares data portal operation methods (methods marked with `[Create]`, `[Fetch]`, `[Insert]`, `[Update]`, `[Execute]`, `[Delete]`, `[DeleteSelf]`, `[CreateChild]`, `[FetchChild]`, `[InsertChild]`, `[UpdateChild]`, `[DeleteSelfChild]` or `[ExecuteChild]`) is handled, as long as the class and every type that contains it are `partial`: + +```csharp +[CslaImplementProperties] +public partial class PersonEdit : BusinessBase +{ + public partial int Id { get; private set; } + public partial string Name { get; set; } + + [Create] + private void Create() { } + + [Fetch] + private async Task Fetch(int id, [Inject] IPersonDal dal) + { + var data = await dal.GetAsync(id); + // ... + } + + [Update] + private async Task Update([Inject] IPersonDal dal) { /* ... */ } +} +``` + +Analyzer [CSLA0024](analyzers/CSLA0024-TypeWithOperationsShouldBePartialAnalyzer.md) reports classes with operation methods that are not `partial`, and provides a code fix that adds the `partial` modifier. Classes that are not `partial` continue to work through reflection. + +Your operation methods do not change. They can still be `private`, take criteria and `[Inject]` parameters (including keyed services), and be synchronous or asynchronous. + +## What is generated + +For each `partial` class, the generator adds: + +* An internal nested `IDataPortalOperations` interface with a member for each operation method, implemented explicitly by the class. This makes `private` operation methods visible to IDEs, analyzers and the trimmer as used code. +* For concrete classes, implementations of `Csla.Server.IDataPortalOperationMapping` and `Csla.Server.IDataPortalOperationNamedMapping`. The data portal calls these to invoke the operation methods. + +The data portal dispatches an operation in this order: + +1. **By name.** The client sends the name of the operation (for example `Fetch__Int32`), computed from the operation method's criteria parameter types. The generated code uses the name to call the method directly. +2. **By criteria.** If no name is available, for example because the call comes from an older client, the generated code matches the criteria values against the parameter types of each operation method. +3. **By reflection.** If the generated code cannot handle the call, the data portal falls back to the reflection-based dispatch used by earlier versions of CSLA. + +The generated code resolves `[Inject]` parameters and reports errors the same way as reflection-based dispatch. An exception thrown by an operation method is wrapped in a `CallMethodException`, as before. + +## Operation methods that share a name + +Two operation methods map to the same operation when they have the same attribute and the same criteria parameter types, and differ only in their `[Inject]` parameters. As with reflection-based dispatch, the data portal calls the method with more injected parameters, and analyzer [CSLADP002](analyzers/CSLADP002-DuplicateOperationName.md) reports which method is used. If the methods have the same number of injected parameters, the data portal cannot choose between them, and analyzer [CSLADP009](analyzers/CSLADP009-AmbiguousOperationName.md) reports the ambiguity. + +## Related + +* [Data portal extensions generator](data-portal-extensions-generator.md) - strongly typed extension methods for calling the data portal +* [Analyzers](analyzers/index.md) diff --git a/releasenotes.md b/releasenotes.md index 5f00ac3dc4..4b4c227207 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -43,15 +43,15 @@ var person = await portal.PortalFetchAsync(42); ```xml - + false - + none ``` With no async suffix, the sync methods would have the same names as the async methods, so they are not generated. -* The `CSLADP002` to `CSLADP011` diagnostics, about operation methods that share an operation name and about extension methods that are not generated, are reported by analyzers rather than by the source generators, so they can be suppressed in source. +* The `CSLADP002` to `CSLADP011` diagnostics, about operation methods that share an operation name and about extension methods that are not generated, are reported by analyzers rather than by the source generators, so they can be suppressed in source. They are described in the [analyzer documentation](docs/analyzers/index.md#data-portal-generator-diagnostics). * Custom or mock `IDataPortal` implementations continue to work; the generated methods fall back to the existing `params object[]` methods. * Analyzer **CSLA0025** suggests using the generated extension methods instead of calling `IDataPortal` methods with untyped criteria, and provides a code fix that replaces the call. @@ -61,7 +61,6 @@ The data portal extension generator and the CSLA0025 analyzer are adapted from [ * CSLA 11 no longer targets .NET 8 or .NET 9. Applications must target .NET 10 or later (or continue to use .NET Framework 4.6.2 through 4.8, or a `netstandard2.0`-compatible runtime). * The `Csla.Generator.AutoImplementProperties.CSharp` package is retired because its generators are now included in the `Csla` package. Remove any `PackageReference` (and `PackageVersion`) for it; a build error (`CSLABUILD001`) is reported while the reference remains. -* Csla's abstract base classes that declare data portal operation methods (`BusinessListBase`, `CommandBase`, `ReadOnlyBase`, and others) are now `partial`. ### Supported Platforms